From 560886b3d0775dfb312d1815fc21a4c90089d6f3 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 12 Sep 2023 10:19:42 +0200 Subject: [PATCH 001/220] Update to net8 --- .github/workflows/build-and-test.yml | 31 ++++---------- .github/workflows/code-coverage.yml | 2 +- README.md | 4 +- src/ImageSharp/Common/Helpers/Numerics.cs | 19 --------- .../CodeAnalysis/UnscopedRefAttribute.cs | 42 ------------------- .../Formats/Webp/Lossy/LossyUtils.cs | 13 +----- src/ImageSharp/ImageSharp.csproj | 4 +- .../ImageSharp.Benchmarks.csproj | 6 +-- .../ImageSharp.Tests.ProfilingSandbox.csproj | 4 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 4 +- 10 files changed, 21 insertions(+), 108 deletions(-) delete mode 100644 src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b37557401..6f9dc1d42 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -19,44 +19,29 @@ jobs: - ${{ 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 - sdk: 7.0.x + framework: net8.0 + sdk: 8.0.x sdk-preview: true runtime: -x64 codecov: false - os: macos-latest - framework: net7.0 - sdk: 7.0.x + framework: net8.0 + sdk: 8.0.x sdk-preview: true runtime: -x64 codecov: false - os: windows-latest - framework: net7.0 - sdk: 7.0.x + framework: net8.0 + sdk: 8.0.x sdk-preview: true runtime: -x64 codecov: false - os: buildjet-4vcpu-ubuntu-2204-arm - framework: net7.0 - sdk: 7.0.x + framework: net8.0 + sdk: 8.0.x sdk-preview: true runtime: -x64 codecov: false - - os: ubuntu-latest - framework: net6.0 - sdk: 6.0.x - runtime: -x64 - codecov: false - - os: macos-latest - framework: net6.0 - sdk: 6.0.x - runtime: -x64 - codecov: false - - os: windows-latest - framework: net6.0 - sdk: 6.0.x - runtime: -x64 - codecov: false exclude: - isARM: false options: diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index e551afbd6..bbaea3a61 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -10,7 +10,7 @@ jobs: matrix: options: - os: ubuntu-latest - framework: net6.0 + framework: net8.0 runtime: -x64 codecov: true diff --git a/README.md b/README.md index fa51d57cd..cf58b6b14 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Designed to simplify image processing, ImageSharp brings you an incredibly power ImageSharp is designed from the ground up to be flexible and extensible. The library provides API endpoints for common image processing operations and the building blocks to allow for the development of additional operations. -Built against [.NET 6](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios. +Built against [.NET 8](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios. ## License @@ -64,7 +64,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!) - Using [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) - Make sure you have the latest version installed - - Make sure you have [the .NET 7 SDK](https://www.microsoft.com/net/core#windows) installed + - Make sure you have [the .NET 8 SDK](https://www.microsoft.com/net/core#windows) installed Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**: diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index aba3c0abd..293997c4d 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -908,25 +908,6 @@ 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/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs b/src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs deleted file mode 100644 index dc2c7bd19..000000000 --- a/src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. -#if NET6_0 -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Used to indicate a byref escapes and is not scoped. - /// - /// - /// - /// There are several cases where the C# compiler treats a as implicitly - /// - where the compiler does not allow the to escape the method. - /// - /// - /// For example: - /// - /// for instance methods. - /// parameters that refer to types. - /// parameters. - /// - /// - /// - /// This attribute is used in those instances where the should be allowed to escape. - /// - /// - /// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for - /// API authors to understand the lifetime implications of applying this attribute and how it may impact their users. - /// - /// - [global::System.AttributeUsage( - global::System.AttributeTargets.Method | - global::System.AttributeTargets.Property | - global::System.AttributeTargets.Parameter, - AllowMultiple = false, - Inherited = false)] - internal sealed class UnscopedRefAttribute : global::System.Attribute - { - } -} -#endif diff --git a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs index de3f1586a..aae4181ce 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs @@ -230,11 +230,7 @@ internal static class LossyUtils } } -#if NET7_0_OR_GREATER return (int)Vector128.Sum(sum); -#else - return Numerics.ReduceSumArm(sum); -#endif } [MethodImpl(InliningOptions.ShortMethod)] @@ -252,11 +248,7 @@ internal static class LossyUtils } } -#if NET7_0_OR_GREATER return (int)Vector128.Sum(sum); -#else - return Numerics.ReduceSumArm(sum); -#endif } [MethodImpl(InliningOptions.ShortMethod)] @@ -275,11 +267,8 @@ internal static class LossyUtils 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 diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index b08c27c41..12da6cf53 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -29,13 +29,13 @@ - net7.0;net6.0 + net8.0 true - net6.0 + net8.0 true diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 0ba2f4b94..22a2777a6 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -11,7 +11,7 @@ false Debug;Release - + @@ -23,12 +23,12 @@ - net7.0;net6.0 + net8.0 - net6.0 + net8.0 diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 492ce36b8..dfa01b8be 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -19,12 +19,12 @@ - net7.0;net6.0 + net8.0 - net6.0 + net8.0 diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index dc081e0be..491c4da87 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -12,12 +12,12 @@ - net7.0;net6.0 + net8.0 - net6.0 + net8.0 From 807b5d9555eb776615ef6c2049fa6e26c2cc7c11 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 10:56:56 +0100 Subject: [PATCH 002/220] Update build sdk to net8 --- .github/workflows/build-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6f9dc1d42..d4c3888d4 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -96,14 +96,14 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x + 8.0.x - name: DotNet Setup Preview if: ${{ matrix.options.sdk-preview == true }} uses: actions/setup-dotnet@v3 with: dotnet-version: | - 7.0.x + 8.0.x - name: DotNet Build if: ${{ matrix.options.sdk-preview != true }} From 836744f47b300787fdbe97711b2a41b390f3e307 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 11:00:13 +0100 Subject: [PATCH 003/220] use win-x64 as rid --- .github/workflows/code-coverage.yml | 2 +- .../ImageSharp.Tests.ProfilingSandbox.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index bbaea3a61..7624e86b6 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -58,7 +58,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x + 8.0.x - name: DotNet Build shell: pwsh diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index dfa01b8be..76891b4bb 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -7,7 +7,7 @@ Exe false SixLabors.ImageSharp.Tests.ProfilingSandbox - win7-x64 + win-x64 SixLabors.ImageSharp.Tests.ProfilingSandbox.Program false From 0b49cb01271d08919120fb6a53957611ddff56ba Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 13:43:25 +0100 Subject: [PATCH 004/220] Use C# 12.0 --- src/ImageSharp/ImageSharp.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 12da6cf53..a1c7e6175 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -17,6 +17,7 @@ + 12.0 enable Nullable From 69951a59c5ab7d120ac59e78ca91e9c6beb3839d Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 13:55:00 +0100 Subject: [PATCH 005/220] remove sdk-preview --- .github/workflows/build-and-test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d4c3888d4..7c7791347 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -21,25 +21,21 @@ jobs: - os: ubuntu-latest framework: net8.0 sdk: 8.0.x - sdk-preview: true runtime: -x64 codecov: false - os: macos-latest framework: net8.0 sdk: 8.0.x - sdk-preview: true runtime: -x64 codecov: false - os: windows-latest framework: net8.0 sdk: 8.0.x - sdk-preview: true runtime: -x64 codecov: false - os: buildjet-4vcpu-ubuntu-2204-arm framework: net8.0 sdk: 8.0.x - sdk-preview: true runtime: -x64 codecov: false exclude: From 4564831d10dd27ffc123be025e3ee89bffb3c0ab Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 14:13:31 +0100 Subject: [PATCH 006/220] Fix build errors --- .editorconfig | 2 ++ .../Advanced/ParallelRowIterator.Wrappers.cs | 4 ++-- src/ImageSharp/Advanced/ParallelRowIterator.cs | 10 +++++----- src/ImageSharp/Common/Helpers/DebugGuard.cs | 2 ++ src/ImageSharp/Configuration.cs | 5 +---- src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs | 5 +---- .../Allocators/Internals/SharedArrayPoolBuffer{T}.cs | 2 ++ .../PixelFormats/PixelImplementations/Abgr32.cs | 2 +- .../PixelFormats/PixelImplementations/Argb32.cs | 2 +- .../PixelFormats/PixelImplementations/Bgra32.cs | 2 +- .../PixelFormats/PixelImplementations/La16.cs | 2 +- .../PixelFormats/PixelImplementations/La32.cs | 2 +- .../PixelFormats/PixelImplementations/Rgba32.cs | 2 +- .../PixelFormats/PixelImplementations/Rgba64.cs | 4 ++-- .../Processing/Processors/Dithering/ErrorDither.cs | 2 +- .../PixelRowDelegateProcessor{TPixel,TDelegate}.cs | 2 +- .../Processors/Filters/FilterProcessor{TPixel}.cs | 2 +- .../Processors/Quantization/OctreeQuantizer{TPixel}.cs | 2 +- .../Quantization/PaletteQuantizer{TPixel}.cs | 2 +- .../Processors/Quantization/QuantizerUtilities.cs | 2 +- .../Processors/Quantization/WuQuantizer{TPixel}.cs | 2 +- .../ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 1 + 22 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2e3045fb1..ebda51e37 100644 --- a/.editorconfig +++ b/.editorconfig @@ -422,6 +422,8 @@ dotnet_naming_rule.parameters_rule.symbols = parameters_group dotnet_naming_rule.parameters_rule.style = camel_case_style dotnet_naming_rule.parameters_rule.severity = warning + +dotnet_diagnostics.CA1857.severity = none ########################################## # License ########################################## diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs index 9629b0097..a959faa3b 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs @@ -51,7 +51,7 @@ public static partial class ParallelRowIterator for (int y = yMin; y < yMax; y++) { // Skip the safety copy when invoking a potentially impure method on a readonly field - Unsafe.AsRef(this.action).Invoke(y); + Unsafe.AsRef(in this.action).Invoke(y); } } } @@ -102,7 +102,7 @@ public static partial class ParallelRowIterator for (int y = yMin; y < yMax; y++) { - Unsafe.AsRef(this.action).Invoke(y, span); + Unsafe.AsRef(in this.action).Invoke(y, span); } } } diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 657654a84..1284a3a89 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -58,7 +58,7 @@ public static partial class ParallelRowIterator { for (int y = top; y < bottom; y++) { - Unsafe.AsRef(operation).Invoke(y); + Unsafe.AsRef(in operation).Invoke(y); } return; @@ -118,7 +118,7 @@ public static partial class ParallelRowIterator int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); MemoryAllocator allocator = parallelSettings.MemoryAllocator; - int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle); + int bufferLength = Unsafe.AsRef(in operation).GetRequiredBufferLength(rectangle); // Avoid TPL overhead in this trivial case: if (numOfSteps == 1) @@ -128,7 +128,7 @@ public static partial class ParallelRowIterator for (int y = top; y < bottom; y++) { - Unsafe.AsRef(operation).Invoke(y, span); + Unsafe.AsRef(in operation).Invoke(y, span); } return; @@ -245,7 +245,7 @@ public static partial class ParallelRowIterator int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); MemoryAllocator allocator = parallelSettings.MemoryAllocator; - int bufferLength = Unsafe.AsRef(operation).GetRequiredBufferLength(rectangle); + int bufferLength = Unsafe.AsRef(in operation).GetRequiredBufferLength(rectangle); // Avoid TPL overhead in this trivial case: if (numOfSteps == 1) @@ -253,7 +253,7 @@ public static partial class ParallelRowIterator var rows = new RowInterval(top, bottom); using IMemoryOwner buffer = allocator.Allocate(bufferLength); - Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span); + Unsafe.AsRef(in operation).Invoke(in rows, buffer.Memory.Span); return; } diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index be2daa139..990b21c99 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -33,10 +33,12 @@ internal static partial class DebugGuard [Conditional("DEBUG")] public static void NotDisposed(bool isDisposed, string objectName) { +#pragma warning disable CA1513 if (isDisposed) { throw new ObjectDisposedException(objectName); } +#pragma warning restore CA1513 } /// diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 39fcef9c4..1ca5d0a46 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -87,10 +87,7 @@ public sealed class Configuration get => this.streamProcessingBufferSize; set { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(this.StreamProcessingBufferSize)); - } + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); this.streamProcessingBufferSize = value; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 95f7fde32..7fc2a1f45 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -176,10 +176,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// is . private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder, Span buffer) { - if (tableConfigs is null) - { - throw new ArgumentNullException(nameof(tableConfigs)); - } + ArgumentNullException.ThrowIfNull(tableConfigs); int markerlen = 2; diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs index f9434ee94..c0a0c5d27 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs @@ -59,10 +59,12 @@ internal class SharedArrayPoolBuffer : ManagedBufferBase, IRefCounted [MemberNotNull(nameof(Array))] private void CheckDisposed() { +#pragma warning disable CA1513 if (this.Array == null) { throw new ObjectDisposedException("SharedArrayPoolBuffer"); } +#pragma warning restore CA1513 } private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 4891abba8..8bd24c7a0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -129,7 +129,7 @@ public partial struct Abgr32 : IPixel, IPackedVector public uint Abgr { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 0c99adb52..fa8af98a0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -129,7 +129,7 @@ public partial struct Argb32 : IPixel, IPackedVector public uint Argb { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 6b859cda6..d7222f2ef 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -85,7 +85,7 @@ public partial struct Bgra32 : IPixel, IPackedVector public uint Bgra { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 7597677a2..58aeb6189 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -45,7 +45,7 @@ public partial struct La16 : IPixel, IPackedVector /// public ushort PackedValue { - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); set => Unsafe.As(ref this) = value; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index cb8fad228..db7f43329 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -45,7 +45,7 @@ public partial struct La32 : IPixel, IPackedVector public uint PackedValue { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index a652c2b33..75fe8f3f4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -124,7 +124,7 @@ public partial struct Rgba32 : IPixel, IPackedVector public uint Rgba { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 81c959148..75235c900 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -152,7 +152,7 @@ public partial struct Rgba64 : IPixel, IPackedVector public Rgb48 Rgb { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; @@ -162,7 +162,7 @@ public partial struct Rgba64 : IPixel, IPackedVector public ulong PackedValue { [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index 754aac90e..287a8d197 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -143,7 +143,7 @@ public readonly partial struct ErrorDither : IDither, IEquatable, I for (int x = bounds.Left; x < bounds.Right; x++) { ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, (uint)x); - TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel); + TPixel transformed = Unsafe.AsRef(in processor).GetPaletteColor(sourcePixel); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); sourcePixel = transformed; } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index f59b95050..36bb327cf 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -96,7 +96,7 @@ internal sealed class PixelRowDelegateProcessor : ImageProces PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); // Run the user defined pixel shader to the current row of pixels - Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y)); + Unsafe.AsRef(in this.rowProcessor).Invoke(span, new Point(this.startX, y)); PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers); } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 5ad245e3c..510913964 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -78,7 +78,7 @@ internal class FilterProcessor : ImageProcessor Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length); PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale); - ColorNumerics.Transform(span, ref Unsafe.AsRef(this.matrix)); + ColorNumerics.Transform(span, ref Unsafe.AsRef(in this.matrix)); PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, PixelConversionModifiers.Scale); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs index 1136fbc9d..e81ad59d4 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs @@ -128,7 +128,7 @@ public struct OctreeQuantizer : IQuantizer /// [MethodImpl(InliningOptions.ShortMethod)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) - => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); + => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs index 3df80ea9b..092975d28 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -56,7 +56,7 @@ internal readonly struct PaletteQuantizer : IQuantizer /// [MethodImpl(InliningOptions.ShortMethod)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) - => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); + => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs index 53203f94a..55154d6e2 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs @@ -161,7 +161,7 @@ public static class QuantizerUtilities for (int x = bounds.Left; x < bounds.Right; x++) { - destinationRow[x - offsetX] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x], out TPixel _); + destinationRow[x - offsetX] = Unsafe.AsRef(in quantizer).GetQuantizedColor(sourceRow[x], out TPixel _); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index 524153804..46c3f03d6 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -167,7 +167,7 @@ internal struct WuQuantizer : IQuantizer /// [MethodImpl(InliningOptions.ShortMethod)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) - => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); + => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); /// public readonly byte GetQuantizedColor(TPixel color, out TPixel match) diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 22a2777a6..568600311 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -10,6 +10,7 @@ false Debug;Release + 12.0 From 85c0b618b5432a1c31d41558f59913bc6966d760 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 14:17:10 +0100 Subject: [PATCH 007/220] Disable CS1859 --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index ebda51e37..200753291 100644 --- a/.editorconfig +++ b/.editorconfig @@ -424,6 +424,7 @@ dotnet_naming_rule.parameters_rule.severity = warning dotnet_diagnostics.CA1857.severity = none +dotnet_diagnostics.CA1859.severity = none ########################################## # License ########################################## From 1be9491023c05000952d8f20c55fa57d3a4fe986 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 14:23:16 +0100 Subject: [PATCH 008/220] Disable the rules in the ruleset --- .editorconfig | 3 --- src/ImageSharp.ruleset | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index 200753291..2e3045fb1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -422,9 +422,6 @@ dotnet_naming_rule.parameters_rule.symbols = parameters_group dotnet_naming_rule.parameters_rule.style = camel_case_style dotnet_naming_rule.parameters_rule.severity = warning - -dotnet_diagnostics.CA1857.severity = none -dotnet_diagnostics.CA1859.severity = none ########################################## # License ########################################## diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index e88c43f83..11f504158 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,7 +1,11 @@  + + + + - \ No newline at end of file + From d1c3c31258e8ce734b53e6d926af5c06005df640 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 18 Nov 2023 14:29:43 +0100 Subject: [PATCH 009/220] Nextfixes --- src/ImageSharp/Formats/ImageFormatManager.cs | 5 +---- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs index 0ee4bc79b..b6b5f8779 100644 --- a/src/ImageSharp/Formats/ImageFormatManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -83,10 +83,7 @@ public class ImageFormatManager lock (HashLock) { - if (!this.imageFormats.Contains(format)) - { - this.imageFormats.Add(format); - } + this.imageFormats.Add(format); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index d8305a3f5..4e8067016 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1938,8 +1938,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // Keywords should not be empty or have leading or trailing whitespace. name = PngConstants.Encoding.GetString(keywordBytes); return !string.IsNullOrWhiteSpace(name) - && !name.StartsWith(" ", StringComparison.Ordinal) - && !name.EndsWith(" ", StringComparison.Ordinal); + && !name.StartsWith(' ') && !name.EndsWith(' '); } private static bool IsXmpTextData(ReadOnlySpan keywordBytes) From 8b2e164c83a7fc910a6f84c686f8dc12e1350291 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 20 Nov 2023 08:17:54 +0100 Subject: [PATCH 010/220] Fix CA1857 --- src/ImageSharp.ruleset | 1 - src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 5 +++-- src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index 11f504158..8327dd756 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -2,7 +2,6 @@ - diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 7caaa5868..373069e9b 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -58,7 +59,7 @@ internal static partial class SimdUtils public static void Shuffle4Reduce( ref ReadOnlySpan source, ref Span dest, - byte control) + [ConstantExpected] byte control) { if (Avx.IsSupported || Sse.IsSupported) { @@ -217,7 +218,7 @@ internal static partial class SimdUtils private static void Shuffle4( ReadOnlySpan source, Span dest, - byte control) + [ConstantExpected] byte control) { if (Avx.IsSupported) { diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs index c1437c05e..83cd3d246 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -20,7 +21,7 @@ internal static partial class SimdUtils public static void Shuffle4( ReadOnlySpan source, Span dest, - byte control) + [ConstantExpected] byte control) { VerifyShuffle4SpanInput(source, dest); From d79e08c6dae35d9fae18a7e5e63ee266e98bcc3a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 1 Dec 2023 14:31:18 +0100 Subject: [PATCH 011/220] Update to latest SharedInfrastructure commit --- shared-infrastructure | 2 +- src/ImageSharp/ImageSharp.csproj | 1 - tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/shared-infrastructure b/shared-infrastructure index 353b9afe3..4e892950d 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 353b9afe32a8000410312d17263407cd7bb82d19 +Subproject commit 4e892950d7f17e5cb64160675d6902545cc3b5c5 diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a1c7e6175..12da6cf53 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -17,7 +17,6 @@ - 12.0 enable Nullable diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 568600311..22a2777a6 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -10,7 +10,6 @@ false Debug;Release - 12.0 From 651763daa705a377006eb073f45fc212a33ed950 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 1 Dec 2023 14:47:42 +0100 Subject: [PATCH 012/220] Fix SA1516 --- src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs | 1 + src/ImageSharp/Formats/AnimatedImageMetadata.cs | 1 + src/ImageSharp/Formats/Qoi/QoiDecoder.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs index 5f4015180..75595e1f7 100644 --- a/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs +++ b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats; + internal class AnimatedImageFrameMetadata { /// diff --git a/src/ImageSharp/Formats/AnimatedImageMetadata.cs b/src/ImageSharp/Formats/AnimatedImageMetadata.cs index d89ec41f0..ac3ca29f4 100644 --- a/src/ImageSharp/Formats/AnimatedImageMetadata.cs +++ b/src/ImageSharp/Formats/AnimatedImageMetadata.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats; + internal class AnimatedImageMetadata { /// diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoder.cs b/src/ImageSharp/Formats/Qoi/QoiDecoder.cs index a54095dfc..5c1bf6ad2 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoder.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoder.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Qoi; + internal class QoiDecoder : ImageDecoder { private QoiDecoder() From 98ffa0fb44040cd0e2ae4d8f922d4ee622fe7879 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 1 Dec 2023 16:26:00 +0100 Subject: [PATCH 013/220] Update Microsoft.DotNet.RemoteExecutor --- tests/Directory.Build.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 3a2944287..85796e0d6 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -22,8 +22,8 @@ - - + + From 290906a752f98cdd678516f04d09467c24ce2608 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 1 Dec 2023 16:29:53 +0100 Subject: [PATCH 014/220] Fix CA1513 --- .../Allocators/Internals/SharedArrayPoolBuffer{T}.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs index c0a0c5d27..02bdf0f48 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs @@ -57,15 +57,7 @@ internal class SharedArrayPoolBuffer : ManagedBufferBase, IRefCounted [Conditional("DEBUG")] [MemberNotNull(nameof(Array))] - private void CheckDisposed() - { -#pragma warning disable CA1513 - if (this.Array == null) - { - throw new ObjectDisposedException("SharedArrayPoolBuffer"); - } -#pragma warning restore CA1513 - } + private void CheckDisposed() => ObjectDisposedException.ThrowIf(this.Array == null, this.Array); private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard { From b52ef56181615d91df24977f66fd48fa2d6ca048 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 3 Dec 2023 21:44:53 +0100 Subject: [PATCH 015/220] Promote PixelTypeInfo to TPixel Fixes #2534 --- src/ImageSharp/Formats/PixelTypeInfo.cs | 32 +-- src/ImageSharp/Image{TPixel}.cs | 8 +- src/ImageSharp/PixelFormats/IPixel.cs | 3 + .../PixelFormats/PixelImplementations/A8.cs | 3 + .../PixelImplementations/Abgr32.cs | 3 + .../PixelImplementations/Argb32.cs | 3 + .../PixelImplementations/Bgr24.cs | 3 + .../PixelImplementations/Bgr565.cs | 3 + .../PixelImplementations/Bgra32.cs | 3 + .../PixelImplementations/Bgra4444.cs | 3 + .../PixelImplementations/Bgra5551.cs | 3 + .../PixelImplementations/Byte4.cs | 3 + .../PixelImplementations/HalfSingle.cs | 3 + .../PixelImplementations/HalfVector2.cs | 3 + .../PixelImplementations/HalfVector4.cs | 3 + .../PixelFormats/PixelImplementations/L16.cs | 3 + .../PixelFormats/PixelImplementations/L8.cs | 3 + .../PixelFormats/PixelImplementations/La16.cs | 3 + .../PixelFormats/PixelImplementations/La32.cs | 3 + .../PixelImplementations/NormalizedByte2.cs | 3 + .../PixelImplementations/NormalizedByte4.cs | 3 + .../PixelImplementations/NormalizedShort2.cs | 3 + .../PixelImplementations/NormalizedShort4.cs | 3 + .../PixelOperations/A8.PixelOperations.cs | 9 +- .../PixelOperations/Abgr32.PixelOperations.cs | 9 +- .../PixelOperations/Argb32.PixelOperations.cs | 9 +- .../PixelOperations/Bgr24.PixelOperations.cs | 9 +- .../PixelOperations/Bgr565.PixelOperations.cs | 9 +- .../PixelOperations/Bgra32.PixelOperations.cs | 9 +- .../Bgra4444.PixelOperations.cs | 9 +- .../Bgra5551.PixelOperations.cs | 9 +- .../PixelOperations/Byte4.PixelOperations.cs | 9 +- .../HalfSingle.PixelOperations.cs | 9 +- .../HalfVector2.PixelOperations.cs | 9 +- .../HalfVector4.PixelOperations.cs | 9 +- .../PixelOperations/L16.PixelOperations.cs | 9 +- .../PixelOperations/L8.PixelOperations.cs | 9 +- .../PixelOperations/La16.PixelOperations.cs | 9 +- .../PixelOperations/La32.PixelOperations.cs | 9 +- .../NormalizedByte2.PixelOperations.cs | 9 +- .../NormalizedByte4.PixelOperations.cs | 9 +- .../NormalizedShort2.PixelOperations.cs | 9 +- .../NormalizedShort4.PixelOperations.cs | 9 +- .../PixelOperations/Rg32.PixelOperations.cs | 9 +- .../PixelOperations/Rgb24.PixelOperations.cs | 5 - .../PixelOperations/Rgb48.PixelOperations.cs | 9 +- .../Rgba1010102.PixelOperations.cs | 9 +- .../PixelOperations/Rgba32.PixelOperations.cs | 6 - .../PixelOperations/Rgba64.PixelOperations.cs | 9 +- .../RgbaVector.PixelOperations.cs | 6 - .../PixelOperations/Short2.PixelOperations.cs | 9 +- .../PixelOperations/Short4.PixelOperations.cs | 9 +- .../PixelFormats/PixelImplementations/Rg32.cs | 3 + .../PixelImplementations/Rgb24.cs | 3 + .../PixelImplementations/Rgb48.cs | 3 + .../PixelImplementations/Rgba1010102.cs | 3 + .../PixelImplementations/Rgba32.cs | 3 + .../PixelImplementations/Rgba64.cs | 3 + .../PixelImplementations/RgbaVector.cs | 3 + .../PixelImplementations/Short2.cs | 3 + .../PixelImplementations/Short4.cs | 3 + .../PixelFormats/PixelOperations{TPixel}.cs | 3 +- .../Resize/ResizeProcessor{TPixel}.cs | 2 +- .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Tiff/BigTiffDecoderTests.cs | 2 +- ...elOperationsTests.Specialized.Generated.cs | 263 ++++-------------- .../Generated/_Common.ttinclude | 9 +- .../PixelOperations/PixelOperationsTests.cs | 6 +- tests/ImageSharp.Tests/TestFormat.cs | 2 + .../BasicTestPatternProvider.cs | 5 + 70 files changed, 207 insertions(+), 468 deletions(-) diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index 1328c6528..15fd0dc2a 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats; /// /// Contains information about the pixels that make up an images visual data. /// -public class PixelTypeInfo +public readonly struct PixelTypeInfo { /// /// Initializes a new instance of the class. @@ -21,33 +21,25 @@ public class PixelTypeInfo public PixelTypeInfo(int bitsPerPixel) => this.BitsPerPixel = bitsPerPixel; - /// - /// Initializes a new instance of the class. - /// - /// Color depth, in number of bits per pixel. - /// The pixel alpha transparency behavior. - public PixelTypeInfo(int bitsPerPixel, PixelAlphaRepresentation alpha) - { - this.BitsPerPixel = bitsPerPixel; - this.AlphaRepresentation = alpha; - } - /// /// Gets color depth, in number of bits per pixel. /// - public int BitsPerPixel { get; } + public int BitsPerPixel { get; init; } + + public byte ComponentCount { get; init; } /// /// Gets the pixel alpha transparency behavior. /// means unknown, unspecified. /// - public PixelAlphaRepresentation? AlphaRepresentation { get; } - - internal static PixelTypeInfo Create() - where TPixel : unmanaged, IPixel - => new(Unsafe.SizeOf() * 8); + public PixelAlphaRepresentation? AlphaRepresentation { get; init; } - internal static PixelTypeInfo Create(PixelAlphaRepresentation alpha) + internal static PixelTypeInfo Create(byte componentCount, PixelAlphaRepresentation pixelAlphaRepresentation) where TPixel : unmanaged, IPixel - => new(Unsafe.SizeOf() * 8, alpha); + => new() + { + BitsPerPixel = Unsafe.SizeOf() * 8, + ComponentCount = componentCount, + AlphaRepresentation = pixelAlphaRepresentation + }; } diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index c24014e69..38f0b94d6 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -78,7 +78,7 @@ public sealed class Image : Image /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata) - : base(configuration, PixelTypeInfo.Create(), metadata ?? new(), width, height) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata ?? new(), width, height) => this.frames = new ImageFrameCollection(this, width, height, default(TPixel)); /// @@ -111,7 +111,7 @@ public sealed class Image : Image int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata, width, height) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata, width, height) => this.frames = new ImageFrameCollection(this, width, height, memoryGroup); /// @@ -129,7 +129,7 @@ public sealed class Image : Image int height, TPixel backgroundColor, ImageMetadata? metadata) - : base(configuration, PixelTypeInfo.Create(), metadata ?? new(), width, height) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata ?? new(), width, height) => this.frames = new ImageFrameCollection(this, width, height, backgroundColor); /// @@ -140,7 +140,7 @@ public sealed class Image : Image /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) - : base(configuration, PixelTypeInfo.Create(), metadata, ValidateFramesAndGetSize(frames)) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata, ValidateFramesAndGetSize(frames)) => this.frames = new ImageFrameCollection(this, frames); /// diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 099444466..cddf6453d 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -14,6 +15,8 @@ namespace SixLabors.ImageSharp.PixelFormats; public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { + static abstract PixelTypeInfo GetPixelTypeInfo(); + /// /// Creates a instance for this pixel type. /// This method is not intended to be consumed directly. Use instead. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 025690712..b2d99ac4b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -55,6 +56,8 @@ public partial struct A8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(A8 left, A8 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 8bd24c7a0..7c0dba552 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -183,6 +184,8 @@ public partial struct Abgr32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index fa8af98a0..f09f25ab0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -183,6 +184,8 @@ public partial struct Argb32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index aedf4ad19..499c72b8d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -87,6 +88,8 @@ public partial struct Bgr24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index ac3b6f829..1ac9fbddb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -59,6 +60,8 @@ public partial struct Bgr565 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index d7222f2ef..a689067e5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -136,6 +137,8 @@ public partial struct Bgra32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 8ba32c8ac..de94817d4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -57,6 +58,8 @@ public partial struct Bgra4444 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index c282f03d8..2bfbde7c6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -60,6 +61,8 @@ public partial struct Bgra5551 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index e699e5fe5..e1d6bdd52 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -60,6 +61,8 @@ public partial struct Byte4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index db1e02adc..b37b7260c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -45,6 +46,8 @@ public partial struct HalfSingle : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + /// public PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 9caae58c9..02c3433fd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -52,6 +53,8 @@ public partial struct HalfVector2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 609fec3bd..ee8be97c1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -57,6 +58,8 @@ public partial struct HalfVector4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index c6ee8744d..2b70c1563 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -47,6 +48,8 @@ public partial struct L16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L16 left, L16 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 383e09b27..df6189706 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -48,6 +49,8 @@ public partial struct L8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L8 left, L8 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 58aeb6189..97e5db9f0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -71,6 +72,8 @@ public partial struct La16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La16 left, La16 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index db7f43329..090824a83 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -73,6 +74,8 @@ public partial struct La32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La32 left, La32 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index 92b9e6148..ba56e9ecd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -60,6 +61,8 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index f87bb5a60..27fed194f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -62,6 +63,8 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index f77dd69b7..55ab0888b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -61,6 +62,8 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 989edbd2b..0306a6329 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -63,6 +64,8 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs index a7b4b5df0..131191ee2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct A8 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs index 66f3ecb24..17ca5edd8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Abgr32 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs index 894e92963..f2a5eb28b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Argb32 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs index a8f6ab155..05b198636 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Bgr24 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs index de9690325..7217b7c0b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Bgr565 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs index 1a62b0809..0d77f8566 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Bgra32 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs index 8ffdaf6cb..5f516f094 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Bgra4444 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs index 97f5d805e..ea11e5309 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Bgra5551 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs index f6e0b833c..0946dd4c7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Byte4 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs index c8c4110c7..8343b4b3c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct HalfSingle /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs index bdf58145f..9a2bdd260 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct HalfVector2 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs index c3fe59804..0590b43c8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct HalfVector4 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs index 7495cee53..fc7a81ae2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct L16 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs index 5dd98c3a6..c97af4e34 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct L8 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs index d9bda3e0f..9be38ac4e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct La16 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs index 1fb5adfc8..824618c65 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct La32 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs index 717629586..848d0d6f3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct NormalizedByte2 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs index 9bb48f592..79319070d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct NormalizedByte4 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs index 3913f64bb..0b5f23bc5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct NormalizedShort2 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs index 6334f4e7e..21634a2b3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct NormalizedShort4 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs index a5b803f76..e0e117727 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Rg32 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs index 5473a602f..f88640d9e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs @@ -15,11 +15,6 @@ public partial struct Rgb24 /// internal partial class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; /// internal override void PackFromRgbPlanes( diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs index 56a052a7d..26f216ae2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Rgb48 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs index f55039627..80c54ac2b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Rgba1010102 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs index a4887b393..36b9f85f7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs @@ -18,12 +18,6 @@ public partial struct Rgba32 /// internal partial class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - /// public override void ToVector4( Configuration configuration, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs index 56bbc6b25..f796098c6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Rgba64 /// /// Provides optimized overrides for bulk operations. /// - internal partial class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal partial class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index a3833583f..c18c3d868 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs @@ -19,12 +19,6 @@ public partial struct RgbaVector /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - /// public override void From( Configuration configuration, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs index 435a521ba..737cd5299 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Short2 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs index 546da9c57..ab069c0ab 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs @@ -13,12 +13,5 @@ public partial struct Short4 /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations - { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); - - /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; - } + internal class PixelOperations : PixelOperations; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 0a13a15ed..b824acad4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -57,6 +58,8 @@ public partial struct Rg32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 105618cd9..609ad21b7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -106,6 +107,8 @@ public partial struct Rgb24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 6bf25717c..e89393c52 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -69,6 +70,8 @@ public partial struct Rgb48 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 7bac1d920..734b1e229 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -60,6 +61,8 @@ public partial struct Rgba1010102 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 75fe8f3f4..e1be7d0ec 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -286,6 +287,8 @@ public partial struct Rgba32 : IPixel, IPackedVector return true; } + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 75235c900..95b1f242f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -206,6 +207,8 @@ public partial struct Rgba64 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 899987b71..c914ca9e3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -96,6 +97,8 @@ public partial struct RgbaVector : IPixel /// public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 832e8c770..b5155c1fa 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -64,6 +65,8 @@ public partial struct Short2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index c4dc324a1..331888999 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -66,6 +67,8 @@ public partial struct Short4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index beebec828..ee675e2e2 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.PixelFormats; public partial class PixelOperations where TPixel : unmanaged, IPixel { - private static readonly Lazy LazyInfo = new(() => PixelTypeInfo.Create(), true); private static readonly Lazy> LazyInstance = new(() => default(TPixel).CreatePixelOperations(), true); /// @@ -32,7 +31,7 @@ public partial class PixelOperations /// Gets the pixel type info for the given . /// /// The . - public virtual PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + public static PixelTypeInfo GetPixelTypeInfo() => TPixel.GetPixelTypeInfo(); /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 98c2523fa..38aab0283 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -197,7 +197,7 @@ internal class ResizeProcessor : TransformProcessor, IResampling bool compand, bool premultiplyAlpha) { - PixelAlphaRepresentation? alphaRepresentation = PixelOperations.Instance.GetPixelTypeInfo()?.AlphaRepresentation; + PixelAlphaRepresentation? alphaRepresentation = PixelOperations.GetPixelTypeInfo().AlphaRepresentation; // Premultiply only if alpha representation is unknown or Unassociated: bool needsPremultiplication = alphaRepresentation == null || alphaRepresentation.Value == PixelAlphaRepresentation.Unassociated; diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e76f21d0e..78a7b2c11 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -477,7 +477,7 @@ public class BmpDecoderTests using MemoryStream stream = new(testFile.Bytes, false); ImageInfo imageInfo = Image.Identify(stream); Assert.NotNull(imageInfo); - Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel); + Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 8e90b1dd5..72f53cab7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -65,7 +65,7 @@ public class BigTiffDecoderTests : 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); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs index 5ba5c1bed..b37282927 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. // @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; public partial class PixelOperationsTests { - + public partial class A8_OperationsTests : PixelOperationsTests { public A8_OperationsTests(ITestOutputHelper output) @@ -20,19 +20,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => A8.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = A8.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Argb32_OperationsTests : PixelOperationsTests { public Argb32_OperationsTests(ITestOutputHelper output) @@ -40,19 +35,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Argb32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Argb32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Abgr32_OperationsTests : PixelOperationsTests { public Abgr32_OperationsTests(ITestOutputHelper output) @@ -60,19 +50,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Abgr32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Abgr32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Bgr24_OperationsTests : PixelOperationsTests { public Bgr24_OperationsTests(ITestOutputHelper output) @@ -80,19 +65,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Bgr24.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Bgr24.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Bgr565_OperationsTests : PixelOperationsTests { public Bgr565_OperationsTests(ITestOutputHelper output) @@ -100,19 +80,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Bgr565.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Bgr565.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Bgra32_OperationsTests : PixelOperationsTests { public Bgra32_OperationsTests(ITestOutputHelper output) @@ -120,19 +95,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Bgra32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Bgra32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Bgra4444_OperationsTests : PixelOperationsTests { public Bgra4444_OperationsTests(ITestOutputHelper output) @@ -140,19 +110,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Bgra4444.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Bgra4444.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Bgra5551_OperationsTests : PixelOperationsTests { public Bgra5551_OperationsTests(ITestOutputHelper output) @@ -160,19 +125,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Bgra5551.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Bgra5551.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Byte4_OperationsTests : PixelOperationsTests { public Byte4_OperationsTests(ITestOutputHelper output) @@ -180,19 +140,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Byte4.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Byte4.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class HalfSingle_OperationsTests : PixelOperationsTests { public HalfSingle_OperationsTests(ITestOutputHelper output) @@ -200,19 +155,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => HalfSingle.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = HalfSingle.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class HalfVector2_OperationsTests : PixelOperationsTests { public HalfVector2_OperationsTests(ITestOutputHelper output) @@ -220,19 +170,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => HalfVector2.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = HalfVector2.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class HalfVector4_OperationsTests : PixelOperationsTests { public HalfVector4_OperationsTests(ITestOutputHelper output) @@ -240,19 +185,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => HalfVector4.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = HalfVector4.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class L16_OperationsTests : PixelOperationsTests { public L16_OperationsTests(ITestOutputHelper output) @@ -260,19 +200,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => L16.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = L16.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class L8_OperationsTests : PixelOperationsTests { public L8_OperationsTests(ITestOutputHelper output) @@ -280,19 +215,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => L8.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = L8.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class La16_OperationsTests : PixelOperationsTests { public La16_OperationsTests(ITestOutputHelper output) @@ -300,19 +230,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => La16.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = La16.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class La32_OperationsTests : PixelOperationsTests { public La32_OperationsTests(ITestOutputHelper output) @@ -320,19 +245,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => La32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = La32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class NormalizedByte2_OperationsTests : PixelOperationsTests { public NormalizedByte2_OperationsTests(ITestOutputHelper output) @@ -340,19 +260,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => NormalizedByte2.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = NormalizedByte2.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class NormalizedByte4_OperationsTests : PixelOperationsTests { public NormalizedByte4_OperationsTests(ITestOutputHelper output) @@ -360,19 +275,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => NormalizedByte4.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = NormalizedByte4.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class NormalizedShort2_OperationsTests : PixelOperationsTests { public NormalizedShort2_OperationsTests(ITestOutputHelper output) @@ -380,19 +290,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => NormalizedShort2.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = NormalizedShort2.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class NormalizedShort4_OperationsTests : PixelOperationsTests { public NormalizedShort4_OperationsTests(ITestOutputHelper output) @@ -400,19 +305,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => NormalizedShort4.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = NormalizedShort4.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Rg32_OperationsTests : PixelOperationsTests { public Rg32_OperationsTests(ITestOutputHelper output) @@ -420,19 +320,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rg32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rg32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Rgb24_OperationsTests : PixelOperationsTests { public Rgb24_OperationsTests(ITestOutputHelper output) @@ -440,19 +335,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rgb24.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rgb24.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Rgb48_OperationsTests : PixelOperationsTests { public Rgb48_OperationsTests(ITestOutputHelper output) @@ -460,19 +350,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rgb48.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rgb48.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Rgba1010102_OperationsTests : PixelOperationsTests { public Rgba1010102_OperationsTests(ITestOutputHelper output) @@ -480,19 +365,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rgba1010102.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rgba1010102.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Rgba32_OperationsTests : PixelOperationsTests { public Rgba32_OperationsTests(ITestOutputHelper output) @@ -500,19 +380,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rgba32.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rgba32.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Rgba64_OperationsTests : PixelOperationsTests { public Rgba64_OperationsTests(ITestOutputHelper output) @@ -520,19 +395,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Rgba64.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Rgba64.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class RgbaVector_OperationsTests : PixelOperationsTests { public RgbaVector_OperationsTests(ITestOutputHelper output) @@ -540,19 +410,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => RgbaVector.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = RgbaVector.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - + public partial class Short2_OperationsTests : PixelOperationsTests { public Short2_OperationsTests(ITestOutputHelper output) @@ -560,19 +425,14 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Short2.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Short2.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - + public partial class Short4_OperationsTests : PixelOperationsTests { public Short4_OperationsTests(ITestOutputHelper output) @@ -580,15 +440,10 @@ public partial class PixelOperationsTests { } - protected override PixelOperations Operations => Short4.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = Short4.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude index 0e7b1f335..90cb3a866 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude @@ -70,7 +70,7 @@ using Xunit.Abstractions; void GenerateSpecializedClass(string pixelType, string alpha) {#> - + public partial class <#=pixelType#>_OperationsTests : PixelOperationsTests<<#=pixelType#>> { public <#=pixelType#>_OperationsTests(ITestOutputHelper output) @@ -78,15 +78,10 @@ using Xunit.Abstractions; { } - protected override PixelOperations<<#=pixelType#>> Operations => <#=pixelType#>.PixelOperations.Instance; - - [Fact] - public void IsSpecialImplementation() => Assert.IsType<<#=pixelType#>.PixelOperations>(PixelOperations<<#=pixelType#>>.Instance); - [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = <#=pixelType#>.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(<#=alpha#>, alphaRepresentation); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index a9b3ee9a4..8c16a72df 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -70,7 +70,7 @@ public abstract class PixelOperationsTests : MeasureFixture protected virtual PixelOperations Operations { get; } = PixelOperations.Instance; - protected bool HasUnassociatedAlpha => this.Operations.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated; + protected bool HasUnassociatedAlpha => TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated; internal static TPixel[] CreateExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { @@ -105,7 +105,7 @@ public abstract class PixelOperationsTests : MeasureFixture [Fact] public void PixelTypeInfoHasCorrectBitsPerPixel() { - int bits = this.Operations.GetPixelTypeInfo().BitsPerPixel; + int bits = TPixel.GetPixelTypeInfo().BitsPerPixel; Assert.Equal(Unsafe.SizeOf() * 8, bits); } @@ -123,7 +123,7 @@ public abstract class PixelOperationsTests : MeasureFixture Rgba32 dest = default; pixel.ToRgba32(ref dest); - bool hasAlpha = this.Operations.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; + bool hasAlpha = TPixel.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; byte expectedAlpha = hasAlpha ? Alpha : NoAlpha; Assert.Equal(expectedAlpha, dest.A); diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index f597b708d..465a21970 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -263,6 +263,8 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public struct TestPixelForAgnosticDecode : IPixel { + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public PixelOperations CreatePixelOperations() => new(); public void FromScaledVector4(Vector4 vector) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index 1e3ad3a5d..5fb3501e3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Tests; public abstract partial class TestImageProvider : IXunitSerializable { + [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public TestImageProvider() + { + } + public virtual TPixel GetExpectedBasicTestPatternPixelAt(int x, int y) { throw new NotSupportedException("GetExpectedBasicTestPatternPixelAt(x,y) only works with BasicTestPattern"); From 9ac4cd91a417fa28bbb6357e1e82f63a40cab1d9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 4 Dec 2023 12:33:35 +1000 Subject: [PATCH 016/220] Enable CA1859 --- .gitattributes | 3 +++ shared-infrastructure | 2 +- src/ImageSharp.ruleset | 3 --- src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 2 +- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitattributes b/.gitattributes index 3647a7063..b5f742ab4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -133,3 +133,6 @@ *.pnm filter=lfs diff=lfs merge=lfs -text *.wbmp filter=lfs diff=lfs merge=lfs -text *.exr filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.cur filter=lfs diff=lfs merge=lfs -text +*.ani filter=lfs diff=lfs merge=lfs -text diff --git a/shared-infrastructure b/shared-infrastructure index 4e892950d..1c526a97e 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 4e892950d7f17e5cb64160675d6902545cc3b5c5 +Subproject commit 1c526a97eea8bcbc7c79de095676f7fb975a9fb1 diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index 8327dd756..d7a147df0 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,9 +1,6 @@  - - - diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index c3cd95c02..f251df3bf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -133,7 +133,7 @@ internal sealed class JpegFrame : IDisposable for (int i = 0; i < this.ComponentCount; i++) { - IJpegComponent component = this.Components[i]; + JpegComponent component = this.Components[i]; component.Init(maxSubFactorH, maxSubFactorV); } } @@ -143,7 +143,7 @@ internal sealed class JpegFrame : IDisposable bool fullScan = this.Progressive || !this.Interleaved; for (int i = 0; i < this.ComponentCount; i++) { - IJpegComponent component = this.Components[i]; + JpegComponent component = this.Components[i]; component.AllocateSpectral(fullScan); } } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 086eef058..114fc12b2 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -71,7 +71,7 @@ internal class DirectoryReader throw TiffThrowHelper.ThrowInvalidHeader(); } - private IList ReadIfds(bool isBigTiff) + private List ReadIfds(bool isBigTiff) { List readers = new(); while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 953ef74af..a4fcd9275 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -472,7 +472,7 @@ internal abstract class BaseExifReader } } - private void Add(IList values, IExifValue exif, object? value) + private void Add(IList values, ExifValue exif, object? value) { if (!exif.TrySetValue(value)) { From b4f9bf963bc67a52257d7ac04a9d802073f69ac0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 4 Dec 2023 14:58:01 +1000 Subject: [PATCH 017/220] Update package dependencies and min ver number --- src/ImageSharp/ImageSharp.csproj | 4 ++-- tests/Directory.Build.targets | 20 +++++++++---------- .../ImageSharp.Benchmarks.csproj | 4 ++-- .../LoadResizeSaveStressRunner.cs | 6 ++---- .../ImageSharp.Tests.ProfilingSandbox.csproj | 2 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 6 +++++- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 12da6cf53..4c08fc017 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -22,8 +22,8 @@ - - 3.1 + + 4.0 diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 85796e0d6..d6b35d003 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -18,20 +18,18 @@ - - - - + + - - - - - - + + + + + + - + diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 22a2777a6..4408159ef 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -41,8 +41,8 @@ - - + + diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index dd9b55e58..a06784f1b 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -269,9 +269,7 @@ public class LoadResizeSaveStressRunner Width = this.ThumbnailSize, Height = this.ThumbnailSize, ResizeMode = CropScaleMode.Max, - SaveFormat = FileFormat.Jpeg, - JpegQuality = Quality, - JpegSubsampleMode = ChromaSubsampleMode.Subsample420 + EncoderOptions = new JpegEncoderOptions(Quality, ChromaSubsampleMode.Subsample420, true) }; // TODO: Is there a way to capture input dimensions for IncreaseTotalMegapixels? @@ -343,6 +341,6 @@ public class LoadResizeSaveStressRunner using var thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize); // Save the results - thumb.Jpegsave(this.OutputPath(input), q: Quality, strip: true); + thumb.Jpegsave(this.OutputPath(input), q: Quality, keep: NetVips.Enums.ForeignKeep.None); } } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 76891b4bb..b93d01191 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -43,7 +43,7 @@ - + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 491c4da87..e0c65475a 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -35,7 +35,11 @@ - + + From 2bf8d78f0fd7fd38f95f5cf99b2e0c9e01554b98 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 4 Dec 2023 10:52:24 +0100 Subject: [PATCH 018/220] Fix Analyzer errors --- src/ImageSharp.ruleset | 3 +++ src/ImageSharp/Formats/PixelTypeInfo.cs | 5 ++++- src/ImageSharp/PixelFormats/IPixel.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/A8.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs | 4 ++++ .../PixelFormats/PixelImplementations/HalfSingle.cs | 4 ++++ .../PixelFormats/PixelImplementations/HalfVector2.cs | 4 ++++ .../PixelFormats/PixelImplementations/HalfVector4.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/L16.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/L8.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/La16.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/La32.cs | 4 ++++ .../PixelFormats/PixelImplementations/NormalizedByte2.cs | 4 ++++ .../PixelFormats/PixelImplementations/NormalizedByte4.cs | 4 ++++ .../PixelFormats/PixelImplementations/NormalizedShort2.cs | 4 ++++ .../PixelFormats/PixelImplementations/NormalizedShort4.cs | 4 ++++ .../PixelOperations/Rgb24.PixelOperations.cs | 1 - src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs | 4 ++++ .../PixelFormats/PixelImplementations/Rgba1010102.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs | 4 ++++ .../PixelFormats/PixelImplementations/RgbaVector.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs | 4 ++++ src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs | 4 ++++ 33 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index d7a147df0..856838ade 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,6 +1,9 @@  + + + diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index 15fd0dc2a..ba26e48ee 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats; public readonly struct PixelTypeInfo { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// Color depth, in number of bits per pixel. public PixelTypeInfo(int bitsPerPixel) @@ -26,6 +26,9 @@ public readonly struct PixelTypeInfo /// public int BitsPerPixel { get; init; } + /// + /// Gets the count of the color components + /// public byte ComponentCount { get; init; } /// diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index cddf6453d..ed02bc917 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -15,6 +15,10 @@ namespace SixLabors.ImageSharp.PixelFormats; public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo static abstract PixelTypeInfo GetPixelTypeInfo(); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index b2d99ac4b..8a641da4b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -56,6 +56,10 @@ public partial struct A8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(A8 left, A8 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 7c0dba552..692258add 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -184,6 +184,10 @@ public partial struct Abgr32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index f09f25ab0..1ed505ce0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -184,6 +184,10 @@ public partial struct Argb32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 499c72b8d..16f3a3aa3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -88,6 +88,10 @@ public partial struct Bgr24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 1ac9fbddb..76ba91d26 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -60,6 +60,10 @@ public partial struct Bgr565 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index a689067e5..33b5cbdf8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -137,6 +137,10 @@ public partial struct Bgra32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index de94817d4..551f9ae8b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -58,6 +58,10 @@ public partial struct Bgra4444 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 2bfbde7c6..2477fd017 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -61,6 +61,10 @@ public partial struct Bgra5551 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index e1d6bdd52..de35a345e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -61,6 +61,10 @@ public partial struct Byte4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index b37b7260c..f1a8c175c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -46,6 +46,10 @@ public partial struct HalfSingle : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 02c3433fd..5a0bdb437 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -53,6 +53,10 @@ public partial struct HalfVector2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index ee8be97c1..0f4059461 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -58,6 +58,10 @@ public partial struct HalfVector4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 2b70c1563..f9ec88f8e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -48,6 +48,10 @@ public partial struct L16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L16 left, L16 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index df6189706..5b3b8934b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -49,6 +49,10 @@ public partial struct L8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L8 left, L8 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 97e5db9f0..5d72818ac 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -72,6 +72,10 @@ public partial struct La16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La16 left, La16 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 090824a83..1cf04d157 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -74,6 +74,10 @@ public partial struct La32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La32 left, La32 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index ba56e9ecd..f843d2781 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -61,6 +61,10 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 27fed194f..a3266437d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -63,6 +63,10 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 55ab0888b..2049acf3f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -62,6 +62,10 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 0306a6329..b80ad0a74 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -64,6 +64,10 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs index f88640d9e..b4dd4fcc3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs @@ -15,7 +15,6 @@ public partial struct Rgb24 /// internal partial class PixelOperations : PixelOperations { - /// internal override void PackFromRgbPlanes( ReadOnlySpan redChannel, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index b824acad4..074acf911 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -58,6 +58,10 @@ public partial struct Rg32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 609ad21b7..f4350c786 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -107,6 +107,10 @@ public partial struct Rgb24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index e89393c52..64cbfc046 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -70,6 +70,10 @@ public partial struct Rgb48 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 734b1e229..305144863 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -61,6 +61,10 @@ public partial struct Rgba1010102 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index e1be7d0ec..a6267d771 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -287,6 +287,10 @@ public partial struct Rgba32 : IPixel, IPackedVector return true; } + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 95b1f242f..e564b26f7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -207,6 +207,10 @@ public partial struct Rgba64 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index c914ca9e3..41f564861 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -97,6 +97,10 @@ public partial struct RgbaVector : IPixel /// public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index b5155c1fa..015d334bd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -65,6 +65,10 @@ public partial struct Short2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 331888999..2c85a8d77 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -67,6 +67,10 @@ public partial struct Short4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); + /// + /// Gets the The pixel type information. + /// + /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// From 4b69d06a4b02a3cdc04304438a901de3a8adf83d Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 5 Dec 2023 06:56:35 +0100 Subject: [PATCH 019/220] Made GetPixelTypeInfo non static --- src/ImageSharp.ruleset | 3 --- src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index 856838ade..d7a147df0 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,9 +1,6 @@  - - - diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index ee675e2e2..cf3707f9e 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -31,7 +31,7 @@ public partial class PixelOperations /// Gets the pixel type info for the given . /// /// The . - public static PixelTypeInfo GetPixelTypeInfo() => TPixel.GetPixelTypeInfo(); + public PixelTypeInfo GetPixelTypeInfo() => TPixel.GetPixelTypeInfo(); /// /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. From 2949655d92a8b18f16a7192ad2b5c715297b9386 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 5 Dec 2023 07:02:53 +0100 Subject: [PATCH 020/220] Fix typos --- src/ImageSharp/PixelFormats/IPixel.cs | 6 ++++-- src/ImageSharp/PixelFormats/PixelImplementations/A8.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs | 2 +- .../PixelFormats/PixelImplementations/Bgra4444.cs | 2 +- .../PixelFormats/PixelImplementations/Bgra5551.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs | 2 +- .../PixelFormats/PixelImplementations/HalfSingle.cs | 2 +- .../PixelFormats/PixelImplementations/HalfVector2.cs | 2 +- .../PixelFormats/PixelImplementations/HalfVector4.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/L16.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/L8.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/La16.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/La32.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedByte2.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedByte4.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedShort2.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedShort4.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs | 2 +- .../PixelFormats/PixelImplementations/Rgba1010102.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs | 2 +- .../PixelFormats/PixelImplementations/RgbaVector.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs | 2 +- 30 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index ed02bc917..adbf68861 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -16,10 +16,12 @@ public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// - /// PixelTypeInfo + /// The . +#pragma warning disable CA1000 static abstract PixelTypeInfo GetPixelTypeInfo(); +#pragma warning restore CA1000 /// /// Creates a instance for this pixel type. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 8a641da4b..0dc88ac90 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -57,7 +57,7 @@ public partial struct A8 : IPixel, IPackedVector public static bool operator !=(A8 left, A8 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 692258add..1abf3e79f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -185,7 +185,7 @@ public partial struct Abgr32 : IPixel, IPackedVector public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 1ed505ce0..b5717c052 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -185,7 +185,7 @@ public partial struct Argb32 : IPixel, IPackedVector public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 16f3a3aa3..05d995a4e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -89,7 +89,7 @@ public partial struct Bgr24 : IPixel public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 76ba91d26..14a690a0c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -61,7 +61,7 @@ public partial struct Bgr565 : IPixel, IPackedVector public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 33b5cbdf8..d97f4025a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -138,7 +138,7 @@ public partial struct Bgra32 : IPixel, IPackedVector public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 551f9ae8b..15f84a604 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -59,7 +59,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 2477fd017..109e52d4c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -62,7 +62,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index de35a345e..f900b49d7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -62,7 +62,7 @@ public partial struct Byte4 : IPixel, IPackedVector public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index f1a8c175c..42ba1a309 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -47,7 +47,7 @@ public partial struct HalfSingle : IPixel, IPackedVector public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 5a0bdb437..b9feee519 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -54,7 +54,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 0f4059461..597fa6c46 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -59,7 +59,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index f9ec88f8e..807ff0f0c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -49,7 +49,7 @@ public partial struct L16 : IPixel, IPackedVector public static bool operator !=(L16 left, L16 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 5b3b8934b..999a6f2d1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -50,7 +50,7 @@ public partial struct L8 : IPixel, IPackedVector public static bool operator !=(L8 left, L8 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 5d72818ac..70d1e37d7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -73,7 +73,7 @@ public partial struct La16 : IPixel, IPackedVector public static bool operator !=(La16 left, La16 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 1cf04d157..2d303525d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -75,7 +75,7 @@ public partial struct La32 : IPixel, IPackedVector public static bool operator !=(La32 left, La32 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index f843d2781..e81db6c1a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -62,7 +62,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index a3266437d..79f6f15f5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -64,7 +64,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 2049acf3f..0e4746ca6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -63,7 +63,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index b80ad0a74..a7d470eff 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -65,7 +65,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 074acf911..2a52bd33e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -59,7 +59,7 @@ public partial struct Rg32 : IPixel, IPackedVector public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index f4350c786..4f7cc0d10 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -108,7 +108,7 @@ public partial struct Rgb24 : IPixel public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 64cbfc046..20463ca2f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -71,7 +71,7 @@ public partial struct Rgb48 : IPixel public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 305144863..740f44c85 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -62,7 +62,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index a6267d771..39054e06d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -288,7 +288,7 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index e564b26f7..d013e3fd5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -208,7 +208,7 @@ public partial struct Rgba64 : IPixel, IPackedVector public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 41f564861..fd30103c9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -98,7 +98,7 @@ public partial struct RgbaVector : IPixel public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 015d334bd..697cc78a2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -66,7 +66,7 @@ public partial struct Short2 : IPixel, IPackedVector public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 2c85a8d77..9c205eeef 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -68,7 +68,7 @@ public partial struct Short4 : IPixel, IPackedVector public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); /// - /// Gets the The pixel type information. + /// Gets the pixel type information. /// /// PixelTypeInfo public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); From 6cbbb19a86a816f11b32e9a7f54cb7a457a72ebf Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 5 Dec 2023 07:08:23 +0100 Subject: [PATCH 021/220] Fix usage of static method in ResizeProcessor --- .../Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 38aab0283..cfc30edc0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -197,7 +197,7 @@ internal class ResizeProcessor : TransformProcessor, IResampling bool compand, bool premultiplyAlpha) { - PixelAlphaRepresentation? alphaRepresentation = PixelOperations.GetPixelTypeInfo().AlphaRepresentation; + PixelAlphaRepresentation? alphaRepresentation = PixelOperations.Instance.GetPixelTypeInfo().AlphaRepresentation; // Premultiply only if alpha representation is unknown or Unassociated: bool needsPremultiplication = alphaRepresentation == null || alphaRepresentation.Value == PixelAlphaRepresentation.Unassociated; From 0b2cf3200d0ecd60484345a32ad345b32a893b58 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 5 Dec 2023 07:08:38 +0100 Subject: [PATCH 022/220] Replaced comment with /// --- src/ImageSharp/PixelFormats/PixelImplementations/A8.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs | 5 +---- .../PixelFormats/PixelImplementations/HalfSingle.cs | 5 +---- .../PixelFormats/PixelImplementations/HalfVector2.cs | 5 +---- .../PixelFormats/PixelImplementations/HalfVector4.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/L16.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/L8.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/La16.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/La32.cs | 5 +---- .../PixelFormats/PixelImplementations/NormalizedByte2.cs | 5 +---- .../PixelFormats/PixelImplementations/NormalizedByte4.cs | 5 +---- .../PixelFormats/PixelImplementations/NormalizedShort2.cs | 5 +---- .../PixelFormats/PixelImplementations/NormalizedShort4.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs | 5 +---- .../PixelFormats/PixelImplementations/Rgba1010102.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs | 5 +---- .../PixelFormats/PixelImplementations/RgbaVector.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs | 5 +---- src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs | 5 +---- 29 files changed, 29 insertions(+), 116 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 0dc88ac90..7662495ff 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -56,10 +56,7 @@ public partial struct A8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(A8 left, A8 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 1abf3e79f..8fae07aa1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -184,10 +184,7 @@ public partial struct Abgr32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index b5717c052..9f36e31b8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -184,10 +184,7 @@ public partial struct Argb32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 05d995a4e..4a6caa4ce 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -88,10 +88,7 @@ public partial struct Bgr24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 14a690a0c..564a0c53f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -60,10 +60,7 @@ public partial struct Bgr565 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index d97f4025a..afd6c395e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -137,10 +137,7 @@ public partial struct Bgra32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 15f84a604..81f37c554 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -58,10 +58,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 109e52d4c..c80af59b3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -61,10 +61,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index f900b49d7..6efbc9623 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -61,10 +61,7 @@ public partial struct Byte4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 42ba1a309..8d658b240 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -46,10 +46,7 @@ public partial struct HalfSingle : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index b9feee519..faec2b069 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -53,10 +53,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 597fa6c46..e9a364ed1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -58,10 +58,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 807ff0f0c..63c1c63c5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -48,10 +48,7 @@ public partial struct L16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L16 left, L16 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 999a6f2d1..cc834fefe 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -49,10 +49,7 @@ public partial struct L8 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(L8 left, L8 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 70d1e37d7..53f14d16b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -72,10 +72,7 @@ public partial struct La16 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La16 left, La16 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 2d303525d..ab542c4dc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -74,10 +74,7 @@ public partial struct La32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(La32 left, La32 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index e81db6c1a..bb5238985 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -61,10 +61,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 79f6f15f5..3bc796795 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -63,10 +63,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 0e4746ca6..17aecfbce 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -62,10 +62,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index a7d470eff..6bd7c140b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -64,10 +64,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 2a52bd33e..3f624df4a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -58,10 +58,7 @@ public partial struct Rg32 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 4f7cc0d10..ebb0e4eac 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -107,10 +107,7 @@ public partial struct Rgb24 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 20463ca2f..89f6e32db 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -70,10 +70,7 @@ public partial struct Rgb48 : IPixel [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 740f44c85..9ed14aaad 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -61,10 +61,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 39054e06d..af149e392 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -287,10 +287,7 @@ public partial struct Rgba32 : IPixel, IPackedVector return true; } - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index d013e3fd5..a21407174 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -207,10 +207,7 @@ public partial struct Rgba64 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index fd30103c9..12f7ff267 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -97,10 +97,7 @@ public partial struct RgbaVector : IPixel /// public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 697cc78a2..e5bbeb37f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -65,10 +65,7 @@ public partial struct Short2 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 9c205eeef..c01c4394c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -67,10 +67,7 @@ public partial struct Short4 : IPixel, IPackedVector [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); - /// - /// Gets the pixel type information. - /// - /// PixelTypeInfo + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); /// From 153ccc312eb1f0b8cc8645a84d3aa06081bd1bd2 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 6 Dec 2023 08:42:00 +0100 Subject: [PATCH 023/220] Disable SA1648 and add Ruleset to solution --- ImageSharp.sln | 1 + src/ImageSharp.ruleset | 1 + 2 files changed, 2 insertions(+) diff --git a/ImageSharp.sln b/ImageSharp.sln index 2967acb8f..b8204c47d 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3 src\Directory.Build.props = src\Directory.Build.props src\Directory.Build.targets = src\Directory.Build.targets src\README.md = src\README.md + src\ImageSharp.ruleset = src\ImageSharp.ruleset EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index d7a147df0..6c291bfb1 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -3,5 +3,6 @@ + From bec2c385f36321abf5059fb40bde7e85937d0825 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 9 Dec 2023 06:45:27 +1000 Subject: [PATCH 024/220] Handle dedup of local palette of 256 length (#2607) --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 40 +++++++++++++++---- .../Formats/Gif/MetadataExtensions.cs | 10 ++++- .../Formats/Webp/BitReader/Vp8LBitReader.cs | 4 +- tests/ImageSharp.Tests/TestImages.cs | 4 +- tests/Images/Input/Gif/18-bit_RGB_Cube.gif | 3 ++ 5 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 tests/Images/Input/Gif/18-bit_RGB_Cube.gif diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index f0e1aafd7..9988848d1 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -380,18 +380,42 @@ internal sealed class GifEncoderCore : IImageEncoderInternals // We can use the color data from the decoded metadata here. // We avoid dithering by default to preserve the original colors. ReadOnlyMemory palette = metadata.LocalColorTable.Value; - if (hasDuplicates && !metadata.HasTransparency) { - // A difference was captured but the metadata does not have transparency. + // Duplicates were captured but the metadata does not have transparency. metadata.HasTransparency = true; - transparencyIndex = palette.Length; - metadata.TransparencyIndex = ClampIndex(transparencyIndex); - } - PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex); - using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration, quantizer.Options); - quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds); + if (palette.Length < 256) + { + // We can use the existing palette and set the transparent index as the length. + // decoders will ignore this value. + transparencyIndex = palette.Length; + metadata.TransparencyIndex = ClampIndex(transparencyIndex); + + PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration, quantizer.Options); + quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds); + } + else + { + // We must quantize the frame to generate a local color table. + IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : KnownQuantizers.Octree; + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration, quantizer.Options); + quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds); + + // The transparency index derived by the quantizer will differ from the index + // within the metadata. We need to update the metadata to reflect this. + int derivedTransparencyIndex = GetTransparentIndex(quantized, null); + metadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex); + } + } + else + { + // Just use the local palette. + PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration, quantizer.Options); + quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds); + } } else { diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index 16f788e3d..42602f2c7 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -77,14 +77,20 @@ public static partial class MetadataExtensions } internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source) - => new() + { + // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or + // has a local palette with 256 colors and is not transparent we should use 'Source'. + bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); + + return new() { ColorTable = source.LocalColorTable, ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local, Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), DisposalMode = GetMode(source.DisposalMethod), - BlendMode = source.DisposalMethod == GifDisposalMethod.RestoreToBackground ? FrameBlendMode.Source : FrameBlendMode.Over, + BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, }; + } private static FrameDisposalMode GetMode(GifDisposalMethod method) => method switch { diff --git a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs index 659576cf1..6d3cab151 100644 --- a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs +++ b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs @@ -71,7 +71,7 @@ internal class Vp8LBitReader : BitReaderBase this.Eos = false; ulong currentValue = 0; - System.Span dataSpan = this.Data.Memory.Span; + Span dataSpan = this.Data.Memory.Span; for (int i = 0; i < 8; i++) { currentValue |= (ulong)dataSpan[i] << (8 * i); @@ -103,7 +103,7 @@ internal class Vp8LBitReader : BitReaderBase } ulong currentValue = 0; - System.Span dataSpan = this.Data.Memory.Span; + Span dataSpan = this.Data.Memory.Span; for (int i = 0; i < length; i++) { currentValue |= (ulong)dataSpan[i] << (8 * i); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 7e862f7d4..b62852902 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -487,6 +487,7 @@ public static class TestImages public const string GlobalQuantizationTest = "Gif/GlobalQuantizationTest.gif"; public const string MixedDisposal = "Gif/mixed-disposal.gif"; public const string M4nb = "Gif/m4nb.gif"; + public const string Bit18RGBCube = "Gif/18-bit_RGB_Cube.gif"; // Test images from https://github.com/robert-ancell/pygif/tree/master/test-suite public const string ZeroSize = "Gif/image-zero-size.gif"; @@ -533,7 +534,8 @@ public static class TestImages Issues.Issue2450_A, Issues.Issue2450_B, Issues.BadDescriptorWidth, - Issues.Issue1530 + Issues.Issue1530, + Bit18RGBCube }; } diff --git a/tests/Images/Input/Gif/18-bit_RGB_Cube.gif b/tests/Images/Input/Gif/18-bit_RGB_Cube.gif new file mode 100644 index 000000000..5446661b4 --- /dev/null +++ b/tests/Images/Input/Gif/18-bit_RGB_Cube.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5148c8c192385966ec6ad5b3d35195a878355d71146a958e5ef02b7642a4e883 +size 4311986 From 5c13ca7fa7ab53da21893958e601d755132a45b6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 9 Dec 2023 19:46:32 +1000 Subject: [PATCH 025/220] Replace Crc32, fix benchmarks --- src/ImageSharp/Compression/Zlib/Crc32.Lut.cs | 69 ---- src/ImageSharp/Compression/Zlib/Crc32.cs | 308 ------------------ ...on-generic-polynomials-pclmulqdq-paper.pdf | Bin 384202 -> 0 bytes src/ImageSharp/Formats/Png/PngDecoderCore.cs | 8 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 15 +- src/ImageSharp/ImageSharp.csproj | 4 + .../ImageSharp.Benchmarks/Bulk/FromVector4.cs | 2 +- .../Bulk/FromVector4_Rgb24.cs | 2 +- .../Bulk/ToVector4_Bgra32.cs | 2 +- .../Bulk/ToVector4_Rgb24.cs | 2 +- .../Codecs/Bmp/DecodeBmp.cs | 2 +- .../Codecs/Bmp/EncodeBmp.cs | 2 +- .../Codecs/Bmp/EncodeBmpMultiple.cs | 2 +- .../Codecs/Gif/DecodeGif.cs | 2 +- .../Codecs/Gif/EncodeGif.cs | 2 +- .../Codecs/Gif/EncodeGifMultiple.cs | 2 +- .../ColorConversion/CmykColorConversion.cs | 2 +- .../GrayscaleColorConversion.cs | 2 +- .../ColorConversion/RgbColorConversion.cs | 2 +- .../ColorConversion/YCbCrColorConversion.cs | 2 +- .../ColorConversion/YccKColorConverter.cs | 2 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 2 +- .../Codecs/Jpeg/DecodeJpeg_Aggregate.cs | 2 +- .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 2 +- .../Codecs/Jpeg/IdentifyJpeg.cs | 2 +- .../Codecs/Png/DecodeFilteredPng.cs | 2 +- .../Codecs/Png/DecodePng.cs | 2 +- .../Codecs/Png/EncodeIndexedPng.cs | 2 +- .../Codecs/Png/EncodePng.cs | 2 +- .../Codecs/Tga/DecodeTga.cs | 2 +- .../Codecs/Tga/EncodeTga.cs | 2 +- .../Codecs/Tiff/DecodeTiff.cs | 2 +- .../Codecs/Tiff/EncodeTiff.cs | 2 +- .../Codecs/Webp/DecodeWebp.cs | 2 +- .../Codecs/Webp/EncodeWebp.cs | 2 +- .../Config.HwIntrinsics.cs | 6 +- tests/ImageSharp.Benchmarks/Config.cs | 24 +- .../General/Adler32Benchmark.cs | 2 +- .../General/CopyBuffers.cs | 2 +- .../General/Crc32Benchmark.cs | 70 ---- .../General/IO/BufferedStreams.cs | 2 +- .../General/Vectorization/UInt32ToSingle.cs | 2 +- .../Vectorization/WidenBytesToUInt32.cs | 2 +- .../Processing/BokehBlur.cs | 2 +- .../ImageSharp.Benchmarks/Processing/Crop.cs | 2 +- .../Processing/DetectEdges.cs | 2 +- .../Processing/Diffuse.cs | 2 +- .../Processing/GaussianBlur.cs | 2 +- .../Processing/HistogramEqualization.cs | 2 +- .../Processing/OilPaint.cs | 2 +- .../Processing/Resize.cs | 2 +- .../Processing/Rotate.cs | 2 +- .../ImageSharp.Benchmarks/Processing/Skew.cs | 2 +- .../Formats/Png/Crc32Tests.cs | 66 ---- 54 files changed, 77 insertions(+), 581 deletions(-) delete mode 100644 src/ImageSharp/Compression/Zlib/Crc32.Lut.cs delete mode 100644 src/ImageSharp/Compression/Zlib/Crc32.cs delete mode 100644 src/ImageSharp/Compression/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf delete mode 100644 tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs diff --git a/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs b/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs deleted file mode 100644 index 9145ac4a4..000000000 --- a/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Compression.Zlib; - -/// -/// Contains precalulated tables for scalar calculations. -/// -internal static partial class Crc32 -{ - /// - /// The table of all possible eight bit values for fast scalar lookup. - /// - private static readonly uint[] CrcTable = - { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, - 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, - 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, - 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, - 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, - 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, - 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, - 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, - 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, - 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, - 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, - 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, - 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, - 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, - 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, - 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, - 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, - 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, - 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, - 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, - 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, - 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, - 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, - 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, - 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, - 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, - 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, - 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, - 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, - 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, - 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, - 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, - 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, - 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, - 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, - 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, - 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, - 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, - 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, - 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, - 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, - 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, - 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, - 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, - 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, - 0x2D02EF8D - }; -} diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs deleted file mode 100644 index 2d0a09bd4..000000000 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -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; - -/// -/// Calculates the 32 bit Cyclic Redundancy Check (CRC) checksum of a given buffer -/// according to the IEEE 802.3 specification. -/// -internal static partial class Crc32 -{ - /// - /// The default initial seed value of a Crc32 checksum calculation. - /// - public const uint SeedValue = 0U; - - private const int MinBufferSize = 64; - private const int ChunksizeMask = 15; - - // Definitions of the bit-reflected domain constants k1, k2, k3, etc and - // the CRC32+Barrett polynomials given at the end of the paper. - private static readonly ulong[] K05Poly = - { - 0x0154442bd4, 0x01c6e41596, // k1, k2 - 0x01751997d0, 0x00ccaa009e, // k3, k4 - 0x0163cd6124, 0x0000000000, // k5, k0 - 0x01db710641, 0x01f7011641 // polynomial - }; - - /// - /// Calculates the CRC checksum with the bytes taken from the span. - /// - /// The readonly span of bytes. - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static uint Calculate(ReadOnlySpan buffer) - => Calculate(SeedValue, buffer); - - /// - /// Calculates the CRC checksum with the bytes taken from the span and seed. - /// - /// The input CRC value. - /// The readonly span of bytes. - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static uint Calculate(uint crc, ReadOnlySpan buffer) - { - if (buffer.IsEmpty) - { - return crc; - } - - if (Sse41.IsSupported && Pclmulqdq.IsSupported && buffer.Length >= MinBufferSize) - { - return ~CalculateSse(~crc, buffer); - } - - if (ArmCrc32.Arm64.IsSupported) - { - return ~CalculateArm64(~crc, buffer); - } - - if (ArmCrc32.IsSupported) - { - return ~CalculateArm(~crc, buffer); - } - - return ~CalculateScalar(~crc, buffer); - } - - // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/crc32_simd.c - [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] - private static unsafe uint CalculateSse(uint crc, ReadOnlySpan buffer) - { - int chunksize = buffer.Length & ~ChunksizeMask; - int length = chunksize; - - fixed (byte* bufferPtr = buffer) - { - fixed (ulong* k05PolyPtr = K05Poly) - { - byte* localBufferPtr = bufferPtr; - ulong* localK05PolyPtr = k05PolyPtr; - - // There's at least one block of 64. - Vector128 x1 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x00)); - Vector128 x2 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x10)); - Vector128 x3 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x20)); - Vector128 x4 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x30)); - Vector128 x5; - - x1 = Sse2.Xor(x1, Sse2.ConvertScalarToVector128UInt32(crc).AsUInt64()); - - // k1, k2 - Vector128 x0 = Sse2.LoadVector128(localK05PolyPtr + 0x0); - - localBufferPtr += 64; - length -= 64; - - // Parallel fold blocks of 64, if any. - while (length >= 64) - { - x5 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - Vector128 x6 = Pclmulqdq.CarrylessMultiply(x2, x0, 0x00); - Vector128 x7 = Pclmulqdq.CarrylessMultiply(x3, x0, 0x00); - Vector128 x8 = Pclmulqdq.CarrylessMultiply(x4, x0, 0x00); - - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x11); - x2 = Pclmulqdq.CarrylessMultiply(x2, x0, 0x11); - x3 = Pclmulqdq.CarrylessMultiply(x3, x0, 0x11); - x4 = Pclmulqdq.CarrylessMultiply(x4, x0, 0x11); - - Vector128 y5 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x00)); - Vector128 y6 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x10)); - Vector128 y7 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x20)); - Vector128 y8 = Sse2.LoadVector128((ulong*)(localBufferPtr + 0x30)); - - x1 = Sse2.Xor(x1, x5); - x2 = Sse2.Xor(x2, x6); - x3 = Sse2.Xor(x3, x7); - x4 = Sse2.Xor(x4, x8); - - x1 = Sse2.Xor(x1, y5); - x2 = Sse2.Xor(x2, y6); - x3 = Sse2.Xor(x3, y7); - x4 = Sse2.Xor(x4, y8); - - localBufferPtr += 64; - length -= 64; - } - - // Fold into 128-bits. - // k3, k4 - x0 = Sse2.LoadVector128(k05PolyPtr + 0x2); - - x5 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x11); - x1 = Sse2.Xor(x1, x2); - x1 = Sse2.Xor(x1, x5); - - x5 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x11); - x1 = Sse2.Xor(x1, x3); - x1 = Sse2.Xor(x1, x5); - - x5 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x11); - x1 = Sse2.Xor(x1, x4); - x1 = Sse2.Xor(x1, x5); - - // Single fold blocks of 16, if any. - while (length >= 16) - { - x2 = Sse2.LoadVector128((ulong*)localBufferPtr); - - x5 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x11); - x1 = Sse2.Xor(x1, x2); - x1 = Sse2.Xor(x1, x5); - - localBufferPtr += 16; - length -= 16; - } - - // Fold 128 - bits to 64 - bits. - x2 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x10); - x3 = Vector128.Create(~0, 0, ~0, 0).AsUInt64(); // _mm_setr_epi32 on x86 - x1 = Sse2.ShiftRightLogical128BitLane(x1, 8); - x1 = Sse2.Xor(x1, x2); - - // k5, k0 - x0 = Sse2.LoadScalarVector128(localK05PolyPtr + 0x4); - - x2 = Sse2.ShiftRightLogical128BitLane(x1, 4); - x1 = Sse2.And(x1, x3); - x1 = Pclmulqdq.CarrylessMultiply(x1, x0, 0x00); - x1 = Sse2.Xor(x1, x2); - - // Barret reduce to 32-bits. - // polynomial - x0 = Sse2.LoadVector128(localK05PolyPtr + 0x6); - - x2 = Sse2.And(x1, x3); - x2 = Pclmulqdq.CarrylessMultiply(x2, x0, 0x10); - x2 = Sse2.And(x2, x3); - x2 = Pclmulqdq.CarrylessMultiply(x2, x0, 0x00); - x1 = Sse2.Xor(x1, x2); - - crc = (uint)Sse41.Extract(x1.AsInt32(), 1); - return buffer.Length - chunksize == 0 ? crc : CalculateScalar(crc, buffer[chunksize..]); - } - } - } - - [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) - { - ref uint crcTableRef = ref MemoryMarshal.GetReference(CrcTable.AsSpan()); - ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); - - for (int i = 0; i < buffer.Length; i++) - { - crc = Unsafe.Add(ref crcTableRef, (crc ^ Unsafe.Add(ref bufferRef, i)) & 0xFF) ^ (crc >> 8); - } - - return crc; - } -} diff --git a/src/ImageSharp/Compression/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf b/src/ImageSharp/Compression/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf deleted file mode 100644 index d0eca86b33851a18132d39983b2118e8da3ee7aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 384202 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

{LABC35aZ+t^w!fb%kfniC0^^)fHUEz8(f& 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 Cetk@ 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

  • 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 z chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); - uint validCrc = Crc32.Calculate(chunkType); - validCrc = Crc32.Calculate(validCrc, chunk.Data.GetSpan()); + Crc32 crc32 = new(); + crc32.Append(chunkType); + crc32.Append(chunk.Data.GetSpan()); - if (validCrc != inputCrc) + if (crc32.GetCurrentHashAsUInt32() != inputCrc) { string chunkTypeName = Encoding.ASCII.GetString(chunkType); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index ddef1c9cd..751df49f1 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Binary; +using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; @@ -1363,16 +1364,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable stream.Write(buffer); - uint crc = Crc32.Calculate(buffer[4..]); // Write the type buffer + Crc32 crc32 = new(); + crc32.Append(buffer[4..]); // Write the type buffer if (data.Length > 0 && length > 0) { stream.Write(data, offset, length); - crc = Crc32.Calculate(crc, data.Slice(offset, length)); + crc32.Append(data.Slice(offset, length)); } - BinaryPrimitives.WriteUInt32BigEndian(buffer, crc); + BinaryPrimitives.WriteUInt32BigEndian(buffer, crc32.GetCurrentHashAsUInt32()); stream.Write(buffer, 0, 4); // write the crc } @@ -1395,16 +1397,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable stream.Write(buffer); - uint crc = Crc32.Calculate(buffer[4..]); // Write the type buffer + Crc32 crc32 = new(); + crc32.Append(buffer[4..]); // Write the type buffer if (data.Length > 0 && length > 0) { stream.Write(data, offset, length); - crc = Crc32.Calculate(crc, data.Slice(offset, length)); + crc32.Append(data.Slice(offset, length)); } - BinaryPrimitives.WriteUInt32BigEndian(buffer, crc); + BinaryPrimitives.WriteUInt32BigEndian(buffer, crc32.GetCurrentHashAsUInt32()); stream.Write(buffer, 0, 4); // write the crc } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 4c08fc017..6096bd33e 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -47,6 +47,10 @@ + + + + True diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index 7e6cec201..d70c37ccb 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -14,7 +14,7 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortCore31))] +[Config(typeof(Config.Short))] public abstract class FromVector4 where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs index fe0d2a10a..27fab64dd 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class FromVector4_Rgb24 : FromVector4 { } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs index 2f1064439..934a17dc9 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class ToVector4_Bgra32 : ToVector4 { [Benchmark(Baseline = true)] diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs index 2c700a733..d5d6e31b5 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class ToVector4_Rgb24 : ToVector4 { [Benchmark(Baseline = true)] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs index 5bd806c48..eb5b3c49d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs @@ -9,7 +9,7 @@ using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeBmp { private byte[] bmpBytes; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs index ab6cdf28e..3e6cfa51b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs @@ -9,7 +9,7 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeBmp { private Stream bmpStream; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs index 50afea5a6..fdef1b2bf 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Bmp; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs index 21b193dda..525e9f5e5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs @@ -9,7 +9,7 @@ using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeGif { private byte[] gifBytes; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs index 048c2aadd..c3644221e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs @@ -12,7 +12,7 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeGif { // System.Drawing needs this. diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs index c523b5c20..9557f616c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { [Params(InputImageCategory.AllImages)] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 51cd02bc7..9189bec37 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class CmykColorConversion : ColorConversionBenchmark { public CmykColorConversion() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 8bf26d721..a1d85ef46 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class GrayscaleColorConversion : ColorConversionBenchmark { public GrayscaleColorConversion() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index ba0964421..5e2b6fe86 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class RgbColorConversion : ColorConversionBenchmark { public RgbColorConversion() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index 87e1bf5aa..f8621c250 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class YCbCrColorConversion : ColorConversionBenchmark { public YCbCrColorConversion() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index 136182936..a414b6ed4 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class YccKColorConverter : ColorConversionBenchmark { public YccKColorConverter() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index b5a724529..f5178390f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -10,7 +10,7 @@ using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeJpegParseStreamOnly { [Params(TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr)] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index bece1de5b..389fec88b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; /// An expensive Jpeg benchmark, running on a wide range of input images, /// showing aggregate results. ///
  • -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase { protected override IEnumerable InputImageSubfoldersOrFiles diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 035f800a9..08df2580d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; /// /// Image-specific Jpeg benchmarks /// -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeJpeg_ImageSpecific { private byte[] jpegBytes; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index d5ad59b00..9cc61c741 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class IdentifyJpeg { private byte[] jpegBytes; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs index 986c1431c..57de8068f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeFilteredPng { private byte[] filter0; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs index c23fa25cc..2cf62fccf 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs @@ -9,7 +9,7 @@ using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodePng { private byte[] pngBytes; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs index 26fb0f4a4..a45e7aea9 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; /// Benchmarks saving png files using different quantizers. /// System.Drawing cannot save indexed png files so we cannot compare. ///
    -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeIndexedPng { // System.Drawing needs this. diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs index 5cbe88fee..428791478 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs @@ -10,7 +10,7 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodePng { // System.Drawing needs this. diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index 363ecf908..2ebab5e00 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeTga { private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs index 935706b41..a7f5e3589 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeTga { private MagickImage tgaMagick; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index 83f5fdd21..ecb87e16c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeTiff { private string prevImage; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index ab3b0e95e..d3e394405 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeTiff { private Stream stream; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs index 34a4ad593..6c71a62b5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeWebp { private Configuration configuration; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs index c5fa8d03d..5be46a222 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeWebp { private MagickImage webpMagick; diff --git a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs index 5a0c574f1..92f8917cf 100644 --- a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs +++ b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs @@ -56,7 +56,7 @@ public partial class Config { public HwIntrinsics_SSE_AVX() { - this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) .WithEnvironmentVariables( new EnvironmentVariable(EnableHWIntrinsic, Off), new EnvironmentVariable(FeatureSIMD, Off)) @@ -64,14 +64,14 @@ public partial class Config if (Sse.IsSupported) { - this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) .WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off)) .WithId("2. SSE")); } if (Avx.IsSupported) { - this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60) + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) .WithId("3. AVX")); } } diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index d95f4a202..06e857484 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -29,25 +29,25 @@ public partial class Config : ManualConfig this.SummaryStyle = SummaryStyle.Default.WithMaxParameterColumnWidth(50); } - public class MultiFramework : Config + public class Standard : Config { - public MultiFramework() => this.AddJob( - Job.Default.WithRuntime(CoreRuntime.Core60).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + public Standard() => this.AddJob( + Job.Default.WithRuntime(CoreRuntime.Core80).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } - public class ShortMultiFramework : Config + public class Short : Config { - public ShortMultiFramework() => this.AddJob( - Job.Default.WithRuntime(CoreRuntime.Core60).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); - } - - public class ShortCore31 : Config - { - public ShortCore31() - => this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); + public Short() => this.AddJob( + Job.Default.WithRuntime(CoreRuntime.Core80) + .WithLaunchCount(1) + .WithWarmupCount(3) + .WithIterationCount(3) + .WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } #if OS_WINDOWS +#pragma warning disable CA1416 // Validate platform compatibility private bool IsElevated => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); +#pragma warning restore CA1416 // Validate platform compatibility #endif } diff --git a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs index 1aac3cff5..30023feca 100644 --- a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs @@ -7,7 +7,7 @@ using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; namespace SixLabors.ImageSharp.Benchmarks.General; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class Adler32Benchmark { private byte[] data; diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs index 3929f7c5a..031f9ecf2 100644 --- a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs +++ b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General; /// - Span.CopyTo() has terrible performance on classic .NET Framework /// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning) ///
    -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class CopyBuffers { private byte[] destArray; diff --git a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs deleted file mode 100644 index fdc9e26b6..000000000 --- a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Compression.Zlib; -using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; - -namespace SixLabors.ImageSharp.Benchmarks.General; - -[Config(typeof(Config.ShortMultiFramework))] -public class Crc32Benchmark -{ - private byte[] data; - private readonly SharpCrc32 crc = new SharpCrc32(); - - [Params(1024, 2048, 4096)] - public int Count { get; set; } - - [GlobalSetup] - public void SetUp() - { - this.data = new byte[this.Count]; - new Random(1).NextBytes(this.data); - } - - [Benchmark(Baseline = true)] - public long SharpZipLibCalculate() - { - this.crc.Reset(); - this.crc.Update(this.data); - return this.crc.Value; - } - - [Benchmark] - public long SixLaborsCalculate() - { - return Crc32.Calculate(this.data); - } -} - -// ########## 17/05/2020 ########## -// -// | Method | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |--------------------- |-------------- |------ |-------------:|-------------:|-----------:|------:|--------:|------:|------:|------:|----------:| -// | SharpZipLibCalculate | .NET 4.7.2 | 1024 | 2,797.77 ns | 278.697 ns | 15.276 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET 4.7.2 | 1024 | 2,275.56 ns | 216.100 ns | 11.845 ns | 0.81 | 0.01 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 2.1 | 1024 | 2,923.43 ns | 2,656.882 ns | 145.633 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 2.1 | 1024 | 2,257.79 ns | 75.081 ns | 4.115 ns | 0.77 | 0.04 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 3.1 | 1024 | 2,764.14 ns | 86.281 ns | 4.729 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 3.1 | 1024 | 49.32 ns | 1.813 ns | 0.099 ns | 0.02 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET 4.7.2 | 2048 | 5,603.71 ns | 427.240 ns | 23.418 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET 4.7.2 | 2048 | 4,525.02 ns | 33.931 ns | 1.860 ns | 0.81 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 2.1 | 2048 | 5,563.32 ns | 49.337 ns | 2.704 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 2.1 | 2048 | 4,519.61 ns | 29.837 ns | 1.635 ns | 0.81 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 3.1 | 2048 | 5,543.37 ns | 518.551 ns | 28.424 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 3.1 | 2048 | 89.07 ns | 3.312 ns | 0.182 ns | 0.02 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET 4.7.2 | 4096 | 11,396.95 ns | 373.450 ns | 20.470 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET 4.7.2 | 4096 | 9,070.35 ns | 271.083 ns | 14.859 ns | 0.80 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 2.1 | 4096 | 11,127.81 ns | 239.177 ns | 13.110 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 2.1 | 4096 | 9,050.46 ns | 230.916 ns | 12.657 ns | 0.81 | 0.00 | - | - | - | - | -// | | | | | | | | | | | | | -// | SharpZipLibCalculate | .NET Core 3.1 | 4096 | 11,098.62 ns | 687.978 ns | 37.710 ns | 1.00 | 0.00 | - | - | - | - | -// | SixLaborsCalculate | .NET Core 3.1 | 4096 | 168.11 ns | 3.633 ns | 0.199 ns | 0.02 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs index 2a926d1cd..9560d6ee7 100644 --- a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.IO; namespace SixLabors.ImageSharp.Benchmarks.IO; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class BufferedStreams { private readonly byte[] buffer = CreateTestBytes(); diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs index fc4dc1fa1..5f1f5666d 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs @@ -7,7 +7,7 @@ using BenchmarkDotNet.Attributes; namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class UInt32ToSingle { private float[] data; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs index f391e4201..da7ddae41 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Tuples; namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class WidenBytesToUInt32 { private byte[] source; diff --git a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs index 441500c88..58c23d2fe 100644 --- a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class BokehBlur { [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Processing/Crop.cs b/tests/ImageSharp.Benchmarks/Processing/Crop.cs index d802d3293..0432b7624 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Crop.cs @@ -11,7 +11,7 @@ using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class Crop { [Benchmark(Baseline = true, Description = "System.Drawing Crop")] diff --git a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs index 937190696..c48f3e4f4 100644 --- a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class DetectEdges { private Image image; diff --git a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs index c74c4a858..bf760aa2d 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class Diffuse { [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs index 0123f4d3b..160df21e6 100644 --- a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Samplers; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class GaussianBlur { [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs index 6292b793e..135145a31 100644 --- a/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs +++ b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class HistogramEqualization { private Image image; diff --git a/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs b/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs index 239d5a93b..e3e413fe4 100644 --- a/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs +++ b/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class OilPaint { [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Processing/Resize.cs b/tests/ImageSharp.Benchmarks/Processing/Resize.cs index ae993cd25..05baceb6a 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Resize.cs @@ -12,7 +12,7 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public abstract class Resize where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs index b9b0f6b3d..89d2966d7 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class Rotate { [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Processing/Skew.cs b/tests/ImageSharp.Benchmarks/Processing/Skew.cs index 26f2e7d2d..baeff29a8 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Skew.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class Skew { [Benchmark] diff --git a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs deleted file mode 100644 index ff91590f9..000000000 --- a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Six Labors. -// 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; - -[Trait("Format", "Png")] -public class Crc32Tests -{ - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void CalculateCrc_ReturnsCorrectResultWhenEmpty(uint input) => Assert.Equal(input, Crc32.Calculate(input, default)); - - [Theory] - [InlineData(0)] - [InlineData(8)] - [InlineData(215)] - [InlineData(1024)] - [InlineData(1024 + 15)] - [InlineData(2034)] - [InlineData(4096)] - public void CalculateCrc_MatchesReference(int length) => CalculateCrcAndCompareToReference(length); - - private static void CalculateCrcAndCompareToReference(int length) - { - // 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) - { - 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]); - } - } -} From ea49ba226ac66366bb14bacd6e759ce511c10b51 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 9 Dec 2023 19:58:02 +1000 Subject: [PATCH 026/220] Use shared instances --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 13 ++++++++---- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 21 ++++++++++++------- .../Bulk/ToVector4_Rgba32.cs | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 3d0ba6f97..3eabbdef0 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -121,6 +121,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals ///
    private readonly PngCrcChunkHandling pngCrcChunkHandling; + /// + /// A reusable Crc32 hashing instance. + /// + private readonly Crc32 crc32 = new(); + /// /// Initializes a new instance of the class. /// @@ -1912,11 +1917,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals Span chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); - Crc32 crc32 = new(); - crc32.Append(chunkType); - crc32.Append(chunk.Data.GetSpan()); + this.crc32.Reset(); + this.crc32.Append(chunkType); + this.crc32.Append(chunk.Data.GetSpan()); - if (crc32.GetCurrentHashAsUInt32() != inputCrc) + if (this.crc32.GetCurrentHashAsUInt32() != inputCrc) { string chunkTypeName = Encoding.ASCII.GetString(chunkType); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 751df49f1..e348d7467 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -124,6 +124,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable ///
    private int derivedTransparencyIndex = -1; + /// + /// A reusable Crc32 hashing instance. + /// + private readonly Crc32 crc32 = new(); + /// /// Initializes a new instance of the class. /// @@ -1364,17 +1369,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable stream.Write(buffer); - Crc32 crc32 = new(); - crc32.Append(buffer[4..]); // Write the type buffer + this.crc32.Reset(); + this.crc32.Append(buffer[4..]); // Write the type buffer if (data.Length > 0 && length > 0) { stream.Write(data, offset, length); - crc32.Append(data.Slice(offset, length)); + this.crc32.Append(data.Slice(offset, length)); } - BinaryPrimitives.WriteUInt32BigEndian(buffer, crc32.GetCurrentHashAsUInt32()); + BinaryPrimitives.WriteUInt32BigEndian(buffer, this.crc32.GetCurrentHashAsUInt32()); stream.Write(buffer, 0, 4); // write the crc } @@ -1397,17 +1402,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable stream.Write(buffer); - Crc32 crc32 = new(); - crc32.Append(buffer[4..]); // Write the type buffer + this.crc32.Reset(); + this.crc32.Append(buffer[4..]); // Write the type buffer if (data.Length > 0 && length > 0) { stream.Write(data, offset, length); - crc32.Append(data.Slice(offset, length)); + this.crc32.Append(data.Slice(offset, length)); } - BinaryPrimitives.WriteUInt32BigEndian(buffer, crc32.GetCurrentHashAsUInt32()); + BinaryPrimitives.WriteUInt32BigEndian(buffer, this.crc32.GetCurrentHashAsUInt32()); stream.Write(buffer, 0, 4); // write the crc } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs index 9abf0ed22..1ceae8414 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs @@ -11,7 +11,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortCore31))] +[Config(typeof(Config.Short))] public class ToVector4_Rgba32 : ToVector4 { [Benchmark] From d8857023be06d728cb71220e1f070a592b620c7e Mon Sep 17 00:00:00 2001 From: Scott Williams <166440+tocsoft@users.noreply.github.com> Date: Sun, 10 Dec 2023 11:30:58 +0000 Subject: [PATCH 027/220] Sync 3.1 DrawImage fixes (#2612) * Handle dedup of local palette of 256 length * handle when foreground overhangs bottom of background * prevent potential overflow * reduce to a single par of clamping operations * correctly calculate Rect when negative target set --------- Co-authored-by: James Jackson-South --- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 6 +++ .../Drawing/DrawImageTests.cs | 42 +++++++++++++++++++ .../Drawing/DrawImageTests/Issue2603.png | 3 ++ .../DrawImageTests/Issue2608_NegOffset.png | 3 ++ 4 files changed, 54 insertions(+) create mode 100644 tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2603.png create mode 100644 tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2608_NegOffset.png diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 378ea20fa..0d81270b1 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -87,15 +87,21 @@ internal class DrawImageProcessor : ImageProcessor if (this.BackgroundLocation.X < 0) { foregroundRectangle.Width += this.BackgroundLocation.X; + foregroundRectangle.X -= this.BackgroundLocation.X; left = 0; } if (this.BackgroundLocation.Y < 0) { foregroundRectangle.Height += this.BackgroundLocation.Y; + foregroundRectangle.Y -= this.BackgroundLocation.Y; top = 0; } + // clamp the height/width to the availible space left to prevent overflowing + foregroundRectangle.Width = Math.Min(source.Width - left, foregroundRectangle.Width); + foregroundRectangle.Height = Math.Min(source.Height - top, foregroundRectangle.Height); + int width = foregroundRectangle.Width; int height = foregroundRectangle.Height; if (width <= 0 || height <= 0) diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 8b0db773a..533f0c6de 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -251,4 +251,46 @@ public class DrawImageTests appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } + + [Theory] + [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)] + public void Issue2608_NegOffset(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image foreground = provider.GetImage(); + using Image background = new(100, 100, new Rgba32(0, 255, 255)); + + background.Mutate(c => c.DrawImage(foreground, new Point(-10, -10), new Rectangle(32, 32, 32, 32), 1F)); + + background.DebugSave( + provider, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + background.CompareToReferenceOutput( + provider, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + + [Theory] + [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)] + public void Issue2603(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image foreground = provider.GetImage(); + using Image background = new(100, 100, new Rgba32(0, 255, 255)); + + background.Mutate(c => c.DrawImage(foreground, new Point(80, 80), new Rectangle(32, 32, 32, 32), 1F)); + + background.DebugSave( + provider, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + background.CompareToReferenceOutput( + provider, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } } diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2603.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2603.png new file mode 100644 index 000000000..1940dbba0 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2603.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:307ef8cea7999e615420740bb0a403a64b24f1fff5356c2ac45c1952f84c5e4c +size 508 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2608_NegOffset.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2608_NegOffset.png new file mode 100644 index 000000000..747bbca38 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2608_NegOffset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edfd0e17aa304f5b16ad42a761c044c3d617aaee9b9e1e74674567bbcd74d532 +size 590 From db705814df9786ed45982c38bb0f665c873afc75 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Dec 2023 16:06:03 +1000 Subject: [PATCH 028/220] Add PixelComponentPrecision --- src/ImageSharp/Color/Color.cs | 2 +- src/ImageSharp/Formats/PixelTypeInfo.cs | 28 +++-- .../CieLabPlanarTiffColor{TPixel}.cs | 8 +- .../CieLabTiffColor{TPixel}.cs | 2 +- .../CmykTiffColor{TPixel}.cs | 2 +- .../PixelFormats/PixelComponentPrecision.cs | 50 ++++++++ .../PixelFormats/PixelImplementations/A8.cs | 2 +- .../PixelImplementations/Abgr32.cs | 2 +- .../PixelImplementations/Argb32.cs | 2 +- .../PixelImplementations/Bgr24.cs | 2 +- .../PixelImplementations/Bgr565.cs | 2 +- .../PixelImplementations/Bgra32.cs | 2 +- .../PixelImplementations/Bgra4444.cs | 2 +- .../PixelImplementations/Bgra5551.cs | 2 +- .../PixelImplementations/Byte4.cs | 2 +- .../PixelImplementations/HalfSingle.cs | 2 +- .../PixelImplementations/HalfVector2.cs | 2 +- .../PixelImplementations/HalfVector4.cs | 2 +- .../PixelFormats/PixelImplementations/L16.cs | 2 +- .../PixelFormats/PixelImplementations/L8.cs | 2 +- .../PixelFormats/PixelImplementations/La16.cs | 2 +- .../PixelFormats/PixelImplementations/La32.cs | 2 +- .../PixelImplementations/NormalizedByte2.cs | 2 +- .../PixelImplementations/NormalizedByte4.cs | 2 +- .../PixelImplementations/NormalizedShort2.cs | 2 +- .../PixelImplementations/NormalizedShort4.cs | 2 +- .../Rgba1010102.PixelOperations.cs | 2 - .../PixelFormats/PixelImplementations/Rg32.cs | 2 +- .../PixelImplementations/Rgb24.cs | 2 +- .../PixelImplementations/Rgb48.cs | 2 +- .../PixelImplementations/Rgba1010102.cs | 4 +- .../PixelImplementations/Rgba32.cs | 2 +- .../PixelImplementations/Rgba64.cs | 2 +- .../PixelImplementations/RgbaVector.cs | 2 +- .../PixelImplementations/Short2.cs | 2 +- .../PixelImplementations/Short4.cs | 2 +- .../ImageSharp.Tests/PixelFormats/A8Tests.cs | 28 +++-- .../PixelFormats/Abgr32Tests.cs | 42 ++++--- .../PixelFormats/Argb32Tests.cs | 42 ++++--- .../PixelFormats/Bgr24Tests.cs | 42 ++++--- .../PixelFormats/Bgr565Tests.cs | 60 +++++---- .../PixelFormats/Bgra32Tests.cs | 42 ++++--- .../PixelFormats/Bgra4444Tests.cs | 58 +++++---- .../PixelFormats/Bgra5551Tests.cs | 67 ++++++---- .../PixelFormats/Byte4Tests.cs | 56 +++++---- .../PixelFormats/HalfSingleTests.cs | 22 +++- .../PixelFormats/HalfVector2Tests.cs | 24 +++- .../PixelFormats/HalfVector4Tests.cs | 18 ++- .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 32 +++-- .../ImageSharp.Tests/PixelFormats/L8Tests.cs | 50 +++++--- .../PixelFormats/La16Tests.cs | 50 +++++--- .../PixelFormats/La32Tests.cs | 32 +++-- .../PixelFormats/NormalizedByte2Tests.cs | 20 ++- .../PixelFormats/NormalizedByte4Tests.cs | 56 +++++---- .../PixelFormats/NormalizedShort2Tests.cs | 20 ++- .../PixelFormats/NormalizedShort4Tests.cs | 56 +++++---- .../PixelFormats/Rg32Tests.cs | 20 ++- .../PixelFormats/Rgb24Tests.cs | 36 ++++-- .../PixelFormats/Rgb48Tests.cs | 26 ++-- .../PixelFormats/Rgba1010102Tests.cs | 58 +++++---- .../PixelFormats/Rgba32Tests.cs | 96 +++++++------- .../PixelFormats/Rgba64Tests.cs | 118 ++++++++++-------- .../PixelFormats/RgbaVectorTests.cs | 59 +++++---- .../PixelFormats/Short2Tests.cs | 44 ++++--- .../PixelFormats/Short4Tests.cs | 74 ++++++----- .../PixelFormats/UnPackedPixelTests.cs | 32 ++--- tests/ImageSharp.Tests/TestFormat.cs | 2 +- .../BasicTestPatternProvider.cs | 4 +- 68 files changed, 971 insertions(+), 569 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelComponentPrecision.cs diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index cebceabe0..99458d58a 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -107,7 +107,7 @@ public readonly partial struct Color : IEquatable public static Color FromPixel(TPixel pixel) where TPixel : unmanaged, IPixel { - // Avoid boxing in case we can convert to Rgba64 safely and efficently + // Avoid boxing in case we can convert to Rgba64 safely and efficiently if (typeof(TPixel) == typeof(Rgba64)) { return new((Rgba64)(object)pixel); diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index ba26e48ee..0c2a4f4c8 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -// TODO: Review this class as it's used to represent 2 different things. +// TODO: Review this type as it's used to represent 2 different things. // 1.The encoded image pixel format. // 2. The pixel format of the decoded image. namespace SixLabors.ImageSharp.Formats; @@ -12,37 +12,43 @@ namespace SixLabors.ImageSharp.Formats; /// /// Contains information about the pixels that make up an images visual data. /// -public readonly struct PixelTypeInfo +/// +/// Initializes a new instance of the struct. +/// +/// Color depth, in number of bits per pixel. +public readonly struct PixelTypeInfo(int bitsPerPixel) { - /// - /// Initializes a new instance of the struct. - /// - /// Color depth, in number of bits per pixel. - public PixelTypeInfo(int bitsPerPixel) - => this.BitsPerPixel = bitsPerPixel; - /// /// Gets color depth, in number of bits per pixel. /// - public int BitsPerPixel { get; init; } + public int BitsPerPixel { get; init; } = bitsPerPixel; /// /// Gets the count of the color components /// public byte ComponentCount { get; init; } + /// + /// Gets the pixel component precision. + /// + public PixelComponentPrecision? ComponentPrecision { get; init; } + /// /// Gets the pixel alpha transparency behavior. /// means unknown, unspecified. /// public PixelAlphaRepresentation? AlphaRepresentation { get; init; } - internal static PixelTypeInfo Create(byte componentCount, PixelAlphaRepresentation pixelAlphaRepresentation) + internal static PixelTypeInfo Create( + byte componentCount, + PixelComponentPrecision componentPrecision, + PixelAlphaRepresentation pixelAlphaRepresentation) where TPixel : unmanaged, IPixel => new() { BitsPerPixel = Unsafe.SizeOf() * 8, ComponentCount = componentCount, + ComponentPrecision = componentPrecision, AlphaRepresentation = pixelAlphaRepresentation }; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs index 216d17330..eb2fe353e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs @@ -27,17 +27,17 @@ internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder a = data[1].GetSpan(); Span b = data[2].GetSpan(); - var color = default(TPixel); + TPixel color = default; int offset = 0; for (int y = top; y < top + height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { - var lab = new CieLab((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]); - var rgb = ColorSpaceConverter.ToRgb(lab); + CieLab lab = new((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]); + Rgb rgb = ColorSpaceConverter.ToRgb(lab); - color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); + color.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); pixelRow[x] = color; offset++; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs index b39a64644..f5418774b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs @@ -34,7 +34,7 @@ internal class CieLabTiffColor : TiffBaseColorDecoder CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]); Rgb rgb = ColorSpaceConverter.ToRgb(lab); - color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); + color.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); pixelRow[x] = color; offset += 3; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs index c87051d52..2074fb254 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs @@ -27,7 +27,7 @@ internal class CmykTiffColor : TiffBaseColorDecoder Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255); Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk); - color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); + color.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); pixelRow[x] = color; offset += 4; diff --git a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs new file mode 100644 index 000000000..8b1298657 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides enumeration of the maximum precision of individual components within a pixel format. +/// +public enum PixelComponentPrecision +{ + /// + /// 8-bit signed integer. + /// + SByte, + + /// + /// 8-bit unsigned integer. + /// + Byte, + + /// + /// 16-bit signed integer. + /// + Short, + + /// + /// 16-bit unsigned integer. + /// + UShort, + + /// + /// 32-bit signed integer. + /// + Int, + + /// + /// 32-bit unsigned integer. + /// + UInt, + + /// + /// 16-bit floating point. + /// + Half, + + /// + /// 32-bit floating point. + /// + Float +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 7662495ff..33fa62d1a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -57,7 +57,7 @@ public partial struct A8 : IPixel, IPackedVector public static bool operator !=(A8 left, A8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 8fae07aa1..66523f040 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -185,7 +185,7 @@ public partial struct Abgr32 : IPixel, IPackedVector public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 9f36e31b8..d3375f65f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -185,7 +185,7 @@ public partial struct Argb32 : IPixel, IPackedVector public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 4a6caa4ce..9d0186c83 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -89,7 +89,7 @@ public partial struct Bgr24 : IPixel public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 564a0c53f..6135ab3e7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -61,7 +61,7 @@ public partial struct Bgr565 : IPixel, IPackedVector public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index afd6c395e..7da4fad4f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -138,7 +138,7 @@ public partial struct Bgra32 : IPixel, IPackedVector public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 81f37c554..effc0d6e5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -59,7 +59,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index c80af59b3..14110b1d1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -62,7 +62,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 6efbc9623..f9a36305d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -62,7 +62,7 @@ public partial struct Byte4 : IPixel, IPackedVector public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 8d658b240..7e1997251 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -47,7 +47,7 @@ public partial struct HalfSingle : IPixel, IPackedVector public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Half, PixelAlphaRepresentation.None); /// public PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index faec2b069..1e3854feb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -54,7 +54,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Half, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index e9a364ed1..233160579 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -59,7 +59,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Half, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 63c1c63c5..fa07d650b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -49,7 +49,7 @@ public partial struct L16 : IPixel, IPackedVector public static bool operator !=(L16 left, L16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index cc834fefe..28fb7ec72 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -50,7 +50,7 @@ public partial struct L8 : IPixel, IPackedVector public static bool operator !=(L8 left, L8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 53f14d16b..99203518a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -73,7 +73,7 @@ public partial struct La16 : IPixel, IPackedVector public static bool operator !=(La16 left, La16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index ab542c4dc..a8c0dcd94 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -75,7 +75,7 @@ public partial struct La32 : IPixel, IPackedVector public static bool operator !=(La32 left, La32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index bb5238985..5db53e853 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -62,7 +62,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.SByte, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 3bc796795..4c850e426 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -64,7 +64,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.SByte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 17aecfbce..7fd299d6d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -63,7 +63,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Short, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 6bd7c140b..ff5cd6a98 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -65,7 +65,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Short, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs index 80c54ac2b..8dcbb319f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 3f624df4a..9a2753341 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -59,7 +59,7 @@ public partial struct Rg32 : IPixel, IPackedVector public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index ebb0e4eac..f4c907abf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -108,7 +108,7 @@ public partial struct Rgb24 : IPixel public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 89f6e32db..642883ff0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -71,7 +71,7 @@ public partial struct Rgb48 : IPixel public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 9ed14aaad..5f289577f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed vector type containing unsigned normalized values ranging from 0 to 1. +/// Packed vector type containing 4 unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. /// /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. @@ -62,7 +62,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index af149e392..f775e8ae1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -288,7 +288,7 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index a21407174..84860d66d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -208,7 +208,7 @@ public partial struct Rgba64 : IPixel, IPackedVector public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 12f7ff267..064064f73 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -98,7 +98,7 @@ public partial struct RgbaVector : IPixel public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Float, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index e5bbeb37f..8af952b18 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -66,7 +66,7 @@ public partial struct Short2 : IPixel, IPackedVector public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Short, PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index c01c4394c..4c60887df 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -68,7 +68,7 @@ public partial struct Short4 : IPixel, IPackedVector public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Short, PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index 95574a4d6..008bc652c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -28,8 +30,8 @@ public class A8Tests [Fact] public void A8_Equality() { - var left = new A8(16); - var right = new A8(32); + A8 left = new(16); + A8 right = new(32); Assert.True(left == new A8(16)); Assert.True(left != right); @@ -56,7 +58,7 @@ public class A8Tests public void A8_ToScaledVector4() { // Arrange - var alpha = new A8(.5F); + A8 alpha = new(.5F); // Act Vector4 actual = alpha.ToScaledVector4(); @@ -72,10 +74,10 @@ public class A8Tests public void A8_ToVector4() { // Arrange - var alpha = new A8(.5F); + A8 alpha = new(.5F); // Act - var actual = alpha.ToVector4(); + Vector4 actual = alpha.ToVector4(); // Assert Assert.Equal(0, actual.X); @@ -87,8 +89,8 @@ public class A8Tests [Fact] public void A8_ToRgba32() { - var input = new A8(128); - var expected = new Rgba32(0, 0, 0, 128); + A8 input = new(128); + Rgba32 expected = new(0, 0, 0, 128); Rgba32 actual = default; input.ToRgba32(ref actual); @@ -99,7 +101,7 @@ public class A8Tests public void A8_FromBgra5551() { // arrange - var alpha = default(A8); + A8 alpha = default; byte expected = byte.MaxValue; // act @@ -108,4 +110,14 @@ public class A8Tests // assert Assert.Equal(expected, alpha.PackedValue); } + + [Fact] + public void A8_PixelInformation() + { + PixelTypeInfo info = A8.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(1, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs index 13c84784c..3c185383c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,8 +17,8 @@ public class Abgr32Tests [Fact] public void AreEqual() { - var color1 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var color2 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + Abgr32 color1 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Abgr32 color2 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue); Assert.Equal(color1, color2); } @@ -27,8 +29,8 @@ public class Abgr32Tests [Fact] public void AreNotEqual() { - var color1 = new Abgr32(0, 0, byte.MaxValue, byte.MaxValue); - var color2 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + Abgr32 color1 = new(0, 0, byte.MaxValue, byte.MaxValue); + Abgr32 color2 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue); Assert.NotEqual(color1, color2); } @@ -46,7 +48,7 @@ public class Abgr32Tests [MemberData(nameof(ColorData))] public void Constructor(byte b, byte g, byte r, byte a) { - var p = new Abgr32(r, g, b, a); + Abgr32 p = new(r, g, b, a); Assert.Equal(r, p.R); Assert.Equal(g, p.G); @@ -57,7 +59,7 @@ public class Abgr32Tests [Fact] public unsafe void ByteLayoutIsSequentialBgra() { - var color = new Abgr32(1, 2, 3, 4); + Abgr32 color = new(1, 2, 3, 4); byte* ptr = (byte*)&color; Assert.Equal(4, ptr[0]); @@ -70,8 +72,8 @@ public class Abgr32Tests [MemberData(nameof(ColorData))] public void Equality_WhenTrue(byte r, byte g, byte b, byte a) { - var x = new Abgr32(r, g, b, a); - var y = new Abgr32(r, g, b, a); + Abgr32 x = new(r, g, b, a); + Abgr32 y = new(r, g, b, a); Assert.True(x.Equals(y)); Assert.True(x.Equals((object)y)); @@ -85,8 +87,8 @@ public class Abgr32Tests [InlineData(1, 255, 0, 0, 0, 255, 0, 0)] public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte g2, byte r2, byte a2) { - var x = new Abgr32(r1, g1, b1, a1); - var y = new Abgr32(r2, g2, b2, a2); + Abgr32 x = new(r1, g1, b1, a1); + Abgr32 y = new(r2, g2, b2, a2); Assert.False(x.Equals(y)); Assert.False(x.Equals((object)y)); @@ -95,7 +97,7 @@ public class Abgr32Tests [Fact] public void FromRgba32() { - var abgr = default(Abgr32); + Abgr32 abgr = default; abgr.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, abgr.R); @@ -104,7 +106,7 @@ public class Abgr32Tests Assert.Equal(4, abgr.A); } - private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new( r / 255f, g / 255f, b / 255f, @@ -113,7 +115,7 @@ public class Abgr32Tests [Fact] public void FromVector4() { - var c = default(Abgr32); + Abgr32 c = default; c.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, c.R); @@ -125,7 +127,7 @@ public class Abgr32Tests [Fact] public void ToVector4() { - var abgr = new Abgr32(1, 2, 3, 4); + Abgr32 abgr = new(1, 2, 3, 4); Assert.Equal(Vec(1, 2, 3, 4), abgr.ToVector4()); } @@ -134,7 +136,7 @@ public class Abgr32Tests public void Abgr32_FromBgra5551() { // arrange - var abgr = default(Abgr32); + Abgr32 abgr = default; uint expected = uint.MaxValue; // act @@ -143,4 +145,14 @@ public class Abgr32Tests // assert Assert.Equal(expected, abgr.PackedValue); } + + [Fact] + public void Abgr32_PixelInformation() + { + PixelTypeInfo info = Abgr32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index 70012afb0..f6b818fb5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Argb32Tests [Fact] public void AreEqual() { - var color1 = new Argb32(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Argb32(new Vector4(0.0f)); - var color3 = new Argb32(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new Argb32(1.0f, 0.0f, 1.0f, 1.0f); + Argb32 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Argb32 color2 = new(new Vector4(0.0f)); + Argb32 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + Argb32 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Argb32Tests [Fact] public void AreNotEqual() { - var color1 = new Argb32(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Argb32(new Vector4(1.0f)); - var color3 = new Argb32(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Argb32(1.0f, 1.0f, 0.0f, 1.0f); + Argb32 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Argb32 color2 = new(new Vector4(1.0f)); + Argb32 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Argb32 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -45,25 +47,25 @@ public class Argb32Tests [Fact] public void ConstructorAssignsProperties() { - var color1 = new Argb32(1, .1f, .133f, .864f); + Argb32 color1 = new(1, .1f, .133f, .864f); Assert.Equal(255, color1.R); Assert.Equal((byte)Math.Round(.1f * 255), color1.G); Assert.Equal((byte)Math.Round(.133f * 255), color1.B); Assert.Equal((byte)Math.Round(.864f * 255), color1.A); - var color2 = new Argb32(1, .1f, .133f); + Argb32 color2 = new(1, .1f, .133f); Assert.Equal(255, color2.R); Assert.Equal(Math.Round(.1f * 255), color2.G); Assert.Equal(Math.Round(.133f * 255), color2.B); Assert.Equal(255, color2.A); - var color4 = new Argb32(new Vector3(1, .1f, .133f)); + Argb32 color4 = new(new Vector3(1, .1f, .133f)); Assert.Equal(255, color4.R); Assert.Equal(Math.Round(.1f * 255), color4.G); Assert.Equal(Math.Round(.133f * 255), color4.B); Assert.Equal(255, color4.A); - var color5 = new Argb32(new Vector4(1, .1f, .133f, .5f)); + Argb32 color5 = new(new Vector4(1, .1f, .133f, .5f)); Assert.Equal(255, color5.R); Assert.Equal(Math.Round(.1f * 255), color5.G); Assert.Equal(Math.Round(.133f * 255), color5.B); @@ -93,7 +95,7 @@ public class Argb32Tests public void Argb32_ToScaledVector4() { // arrange - var argb = new Argb32(Vector4.One); + Argb32 argb = new(Vector4.One); // act Vector4 actual = argb.ToScaledVector4(); @@ -110,7 +112,7 @@ public class Argb32Tests { // arrange Vector4 scaled = new Argb32(Vector4.One).ToScaledVector4(); - var pixel = default(Argb32); + Argb32 pixel = default; uint expected = 0xFFFFFFFF; // act @@ -125,7 +127,7 @@ public class Argb32Tests public void Argb32_FromBgra5551() { // arrange - var argb = default(Argb32); + Argb32 argb = default; uint expected = uint.MaxValue; // act @@ -141,4 +143,14 @@ public class Argb32Tests Assert.Equal(Vector4.Zero, new Argb32(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Argb32(Vector4.One * +1234.0f).ToVector4()); } + + [Fact] + public void Argb32_PixelInformation() + { + PixelTypeInfo info = Argb32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 730e3996d..6861dab41 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +14,8 @@ public class Bgr24Tests [Fact] public void AreEqual() { - var color1 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); - var color2 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); + Bgr24 color1 = new(byte.MaxValue, 0, byte.MaxValue); + Bgr24 color2 = new(byte.MaxValue, 0, byte.MaxValue); Assert.Equal(color1, color2); } @@ -21,8 +23,8 @@ public class Bgr24Tests [Fact] public void AreNotEqual() { - var color1 = new Bgr24(byte.MaxValue, 0, 0); - var color2 = new Bgr24(byte.MaxValue, 0, byte.MaxValue); + Bgr24 color1 = new(byte.MaxValue, 0, 0); + Bgr24 color2 = new(byte.MaxValue, 0, byte.MaxValue); Assert.NotEqual(color1, color2); } @@ -33,7 +35,7 @@ public class Bgr24Tests [MemberData(nameof(ColorData))] public void Constructor(byte r, byte g, byte b) { - var p = new Rgb24(r, g, b); + Rgb24 p = new(r, g, b); Assert.Equal(r, p.R); Assert.Equal(g, p.G); @@ -43,7 +45,7 @@ public class Bgr24Tests [Fact] public unsafe void ByteLayoutIsSequentialBgr() { - var color = new Bgr24(1, 2, 3); + Bgr24 color = new(1, 2, 3); byte* ptr = (byte*)&color; Assert.Equal(3, ptr[0]); @@ -55,8 +57,8 @@ public class Bgr24Tests [MemberData(nameof(ColorData))] public void Equals_WhenTrue(byte r, byte g, byte b) { - var x = new Bgr24(r, g, b); - var y = new Bgr24(r, g, b); + Bgr24 x = new(r, g, b); + Bgr24 y = new(r, g, b); Assert.True(x.Equals(y)); Assert.True(x.Equals((object)y)); @@ -69,8 +71,8 @@ public class Bgr24Tests [InlineData(1, 255, 0, 0, 255, 0)] public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) { - var a = new Bgr24(r1, g1, b1); - var b = new Bgr24(r2, g2, b2); + Bgr24 a = new(r1, g1, b1); + Bgr24 b = new(r2, g2, b2); Assert.False(a.Equals(b)); Assert.False(a.Equals((object)b)); @@ -79,7 +81,7 @@ public class Bgr24Tests [Fact] public void FromRgba32() { - var rgb = default(Bgr24); + Bgr24 rgb = default; rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); @@ -87,7 +89,7 @@ public class Bgr24Tests Assert.Equal(3, rgb.B); } - private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new( r / 255f, g / 255f, b / 255f, @@ -96,7 +98,7 @@ public class Bgr24Tests [Fact] public void FromVector4() { - var rgb = default(Bgr24); + Bgr24 rgb = default; rgb.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); @@ -107,7 +109,7 @@ public class Bgr24Tests [Fact] public void ToVector4() { - var rgb = new Bgr24(1, 2, 3); + Bgr24 rgb = new(1, 2, 3); Assert.Equal(Vec(1, 2, 3), rgb.ToVector4()); } @@ -116,7 +118,7 @@ public class Bgr24Tests public void Bgr24_FromBgra5551() { // arrange - var bgr = default(Bgr24); + Bgr24 bgr = default; // act bgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -126,4 +128,14 @@ public class Bgr24Tests Assert.Equal(255, bgr.G); Assert.Equal(255, bgr.B); } + + [Fact] + public void Bgr24_PixelInformation() + { + PixelTypeInfo info = Bgr24.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(3, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 8245a578f..b68b94325 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Bgr565Tests [Fact] public void AreEqual() { - var color1 = new Bgr565(0.0f, 0.0f, 0.0f); - var color2 = new Bgr565(new Vector3(0.0f)); - var color3 = new Bgr565(new Vector3(1.0f, 0.0f, 1.0f)); - var color4 = new Bgr565(1.0f, 0.0f, 1.0f); + Bgr565 color1 = new(0.0f, 0.0f, 0.0f); + Bgr565 color2 = new(new Vector3(0.0f)); + Bgr565 color3 = new(new Vector3(1.0f, 0.0f, 1.0f)); + Bgr565 color4 = new(1.0f, 0.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Bgr565Tests [Fact] public void AreNotEqual() { - var color1 = new Bgr565(0.0f, 0.0f, 0.0f); - var color2 = new Bgr565(new Vector3(1.0f)); - var color3 = new Bgr565(new Vector3(1.0f, 0.0f, 0.0f)); - var color4 = new Bgr565(1.0f, 1.0f, 0.0f); + Bgr565 color1 = new(0.0f, 0.0f, 0.0f); + Bgr565 color2 = new(new Vector3(1.0f)); + Bgr565 color3 = new(new Vector3(1.0f, 0.0f, 0.0f)); + Bgr565 color4 = new(1.0f, 1.0f, 0.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -66,7 +68,7 @@ public class Bgr565Tests public void Bgr565_ToScaledVector4() { // arrange - var bgr = new Bgr565(Vector3.One); + Bgr565 bgr = new(Vector3.One); // act Vector4 actual = bgr.ToScaledVector4(); @@ -84,7 +86,7 @@ public class Bgr565Tests // arrange Vector4 scaled = new Bgr565(Vector3.One).ToScaledVector4(); int expected = 0xFFFF; - var pixel = default(Bgr565); + Bgr565 pixel = default; // act pixel.FromScaledVector4(scaled); @@ -98,7 +100,7 @@ public class Bgr565Tests public void Bgr565_FromBgra5551() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expected = ushort.MaxValue; // act @@ -112,8 +114,8 @@ public class Bgr565Tests public void Bgr565_FromArgb32() { // arrange - var bgr1 = default(Bgr565); - var bgr2 = default(Bgr565); + Bgr565 bgr1 = default; + Bgr565 bgr2 = default; ushort expected1 = ushort.MaxValue; ushort expected2 = ushort.MaxValue; @@ -130,8 +132,8 @@ public class Bgr565Tests public void Bgr565_FromRgba32() { // arrange - var bgr1 = default(Bgr565); - var bgr2 = default(Bgr565); + Bgr565 bgr1 = default; + Bgr565 bgr2 = default; ushort expected1 = ushort.MaxValue; ushort expected2 = ushort.MaxValue; @@ -148,9 +150,9 @@ public class Bgr565Tests public void Bgr565_ToRgba32() { // arrange - var bgra = new Bgr565(Vector3.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgr565 bgra = new(Vector3.One); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act bgra.ToRgba32(ref actual); @@ -162,7 +164,7 @@ public class Bgr565Tests public void Bgra565_FromRgb48() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -176,7 +178,7 @@ public class Bgr565Tests public void Bgra565_FromRgba64() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -190,7 +192,7 @@ public class Bgr565Tests public void Bgr565_FromBgr24() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expected = ushort.MaxValue; // act @@ -204,7 +206,7 @@ public class Bgr565Tests public void Bgr565_FromRgb24() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expected = ushort.MaxValue; // act @@ -218,7 +220,7 @@ public class Bgr565Tests public void Bgr565_FromGrey8() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expected = ushort.MaxValue; // act @@ -232,7 +234,7 @@ public class Bgr565Tests public void Bgr565_FromGrey16() { // arrange - var bgr = default(Bgr565); + Bgr565 bgr = default; ushort expected = ushort.MaxValue; // act @@ -248,4 +250,14 @@ public class Bgr565Tests Assert.Equal(Vector3.Zero, new Bgr565(Vector3.One * -1234F).ToVector3()); Assert.Equal(Vector3.One, new Bgr565(Vector3.One * 1234F).ToVector3()); } + + [Fact] + public void Bgr565_PixelInformation() + { + PixelTypeInfo info = Bgr565.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(3, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 04c8813d5..17d184d1a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,8 +17,8 @@ public class Bgra32Tests [Fact] public void AreEqual() { - var color1 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var color2 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + Bgra32 color1 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Bgra32 color2 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue); Assert.Equal(color1, color2); } @@ -27,8 +29,8 @@ public class Bgra32Tests [Fact] public void AreNotEqual() { - var color1 = new Bgra32(0, 0, byte.MaxValue, byte.MaxValue); - var color2 = new Bgra32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + Bgra32 color1 = new(0, 0, byte.MaxValue, byte.MaxValue); + Bgra32 color2 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue); Assert.NotEqual(color1, color2); } @@ -46,7 +48,7 @@ public class Bgra32Tests [MemberData(nameof(ColorData))] public void Constructor(byte b, byte g, byte r, byte a) { - var p = new Bgra32(r, g, b, a); + Bgra32 p = new(r, g, b, a); Assert.Equal(r, p.R); Assert.Equal(g, p.G); @@ -57,7 +59,7 @@ public class Bgra32Tests [Fact] public unsafe void ByteLayoutIsSequentialBgra() { - var color = new Bgra32(1, 2, 3, 4); + Bgra32 color = new(1, 2, 3, 4); byte* ptr = (byte*)&color; Assert.Equal(3, ptr[0]); @@ -70,8 +72,8 @@ public class Bgra32Tests [MemberData(nameof(ColorData))] public void Equality_WhenTrue(byte b, byte g, byte r, byte a) { - var x = new Bgra32(r, g, b, a); - var y = new Bgra32(r, g, b, a); + Bgra32 x = new(r, g, b, a); + Bgra32 y = new(r, g, b, a); Assert.True(x.Equals(y)); Assert.True(x.Equals((object)y)); @@ -85,8 +87,8 @@ public class Bgra32Tests [InlineData(1, 255, 0, 0, 0, 255, 0, 0)] public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte g2, byte r2, byte a2) { - var x = new Bgra32(r1, g1, b1, a1); - var y = new Bgra32(r2, g2, b2, a2); + Bgra32 x = new(r1, g1, b1, a1); + Bgra32 y = new(r2, g2, b2, a2); Assert.False(x.Equals(y)); Assert.False(x.Equals((object)y)); @@ -95,7 +97,7 @@ public class Bgra32Tests [Fact] public void FromRgba32() { - var bgra = default(Bgra32); + Bgra32 bgra = default; bgra.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, bgra.R); @@ -104,7 +106,7 @@ public class Bgra32Tests Assert.Equal(4, bgra.A); } - private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new( r / 255f, g / 255f, b / 255f, @@ -113,7 +115,7 @@ public class Bgra32Tests [Fact] public void FromVector4() { - var c = default(Bgra32); + Bgra32 c = default; c.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, c.R); @@ -125,7 +127,7 @@ public class Bgra32Tests [Fact] public void ToVector4() { - var rgb = new Bgra32(1, 2, 3, 4); + Bgra32 rgb = new(1, 2, 3, 4); Assert.Equal(Vec(1, 2, 3, 4), rgb.ToVector4()); } @@ -134,7 +136,7 @@ public class Bgra32Tests public void Bgra32_FromBgra5551() { // arrange - var bgra = default(Bgra32); + Bgra32 bgra = default; uint expected = uint.MaxValue; // act @@ -143,4 +145,14 @@ public class Bgra32Tests // assert Assert.Equal(expected, bgra.PackedValue); } + + [Fact] + public void Bgra32_PixelInformation() + { + PixelTypeInfo info = Bgra32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index 543b5814f..648ef79d5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Bgra4444Tests [Fact] public void AreEqual() { - var color1 = new Bgra4444(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Bgra4444(new Vector4(0.0f)); - var color3 = new Bgra4444(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new Bgra4444(1.0f, 0.0f, 1.0f, 1.0f); + Bgra4444 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Bgra4444 color2 = new(new Vector4(0.0f)); + Bgra4444 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + Bgra4444 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Bgra4444Tests [Fact] public void AreNotEqual() { - var color1 = new Bgra4444(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Bgra4444(new Vector4(1.0f)); - var color3 = new Bgra4444(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Bgra4444(1.0f, 1.0f, 0.0f, 1.0f); + Bgra4444 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Bgra4444 color2 = new(new Vector4(1.0f)); + Bgra4444 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Bgra4444 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -66,7 +68,7 @@ public class Bgra4444Tests public void Bgra4444_ToScaledVector4() { // arrange - var bgra = new Bgra4444(Vector4.One); + Bgra4444 bgra = new(Vector4.One); // act Vector4 actual = bgra.ToScaledVector4(); @@ -82,9 +84,9 @@ public class Bgra4444Tests public void Bgra4444_ToRgba32() { // arrange - var bgra = new Bgra4444(Vector4.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgra4444 bgra = new(Vector4.One); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act bgra.ToRgba32(ref actual); @@ -98,7 +100,7 @@ public class Bgra4444Tests // arrange Vector4 scaled = new Bgra4444(Vector4.One).ToScaledVector4(); int expected = 0xFFFF; - var bgra = default(Bgra4444); + Bgra4444 bgra = default; // act bgra.FromScaledVector4(scaled); @@ -112,7 +114,7 @@ public class Bgra4444Tests public void Bgra4444_FromBgra5551() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expected = ushort.MaxValue; // act @@ -126,7 +128,7 @@ public class Bgra4444Tests public void Bgra4444_FromArgb32() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -140,8 +142,8 @@ public class Bgra4444Tests public void Bgra4444_FromRgba32() { // arrange - var bgra1 = default(Bgra4444); - var bgra2 = default(Bgra4444); + Bgra4444 bgra1 = default; + Bgra4444 bgra2 = default; ushort expectedPackedValue1 = ushort.MaxValue; ushort expectedPackedValue2 = 0xFF0F; @@ -158,7 +160,7 @@ public class Bgra4444Tests public void Bgra4444_FromRgb48() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -172,7 +174,7 @@ public class Bgra4444Tests public void Bgra4444_FromRgba64() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -186,7 +188,7 @@ public class Bgra4444Tests public void Bgra4444_FromGrey16() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -200,7 +202,7 @@ public class Bgra4444Tests public void Bgra4444_FromGrey8() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -214,7 +216,7 @@ public class Bgra4444Tests public void Bgra4444_FromBgr24() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -228,7 +230,7 @@ public class Bgra4444Tests public void Bgra4444_FromRgb24() { // arrange - var bgra = default(Bgra4444); + Bgra4444 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -244,4 +246,14 @@ public class Bgra4444Tests Assert.Equal(Vector4.Zero, new Bgra4444(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Bgra4444(Vector4.One * 1234.0f).ToVector4()); } + + [Fact] + public void Bgra4444_PixelInformation() + { + PixelTypeInfo info = Bgra4444.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index ec5417310..e32ec665e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Bgra5551Tests [Fact] public void AreEqual() { - var color1 = new Bgra5551(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Bgra5551(new Vector4(0.0f)); - var color3 = new Bgra5551(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Bgra5551(1.0f, 0.0f, 0.0f, 1.0f); + Bgra5551 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Bgra5551 color2 = new(new Vector4(0.0f)); + Bgra5551 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Bgra5551 color4 = new(1.0f, 0.0f, 0.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Bgra5551Tests [Fact] public void AreNotEqual() { - var color1 = new Bgra5551(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Bgra5551(new Vector4(1.0f)); - var color3 = new Bgra5551(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Bgra5551(1.0f, 1.0f, 0.0f, 1.0f); + Bgra5551 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Bgra5551 color2 = new(new Vector4(1.0f)); + Bgra5551 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Bgra5551 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -71,7 +73,7 @@ public class Bgra5551Tests public void Bgra5551_ToScaledVector4() { // arrange - var bgra = new Bgra5551(Vector4.One); + Bgra5551 bgra = new(Vector4.One); // act Vector4 actual = bgra.ToScaledVector4(); @@ -87,9 +89,9 @@ public class Bgra5551Tests public void Bgra5551_ToRgba32() { // arrange - var bgra = new Bgra5551(Vector4.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgra5551 bgra = new(Vector4.One); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act bgra.ToRgba32(ref actual); @@ -103,7 +105,7 @@ public class Bgra5551Tests // arrange Vector4 scaled = new Bgra5551(Vector4.One).ToScaledVector4(); int expected = 0xFFFF; - var pixel = default(Bgra5551); + Bgra5551 pixel = default; // act pixel.FromScaledVector4(scaled); @@ -117,9 +119,9 @@ public class Bgra5551Tests public void Bgra5551_FromBgra5551() { // arrange - var bgra = default(Bgra5551); - var actual = default(Bgra5551); - var expected = new Bgra5551(1.0f, 0.0f, 1.0f, 1.0f); + Bgra5551 bgra = default; + Bgra5551 actual = default; + Bgra5551 expected = new(1.0f, 0.0f, 1.0f, 1.0f); // act bgra.FromBgra5551(expected); @@ -133,8 +135,8 @@ public class Bgra5551Tests public void Bgra5551_FromRgba32() { // arrange - var bgra1 = default(Bgra5551); - var bgra2 = default(Bgra5551); + Bgra5551 bgra1 = default; + Bgra5551 bgra2 = default; ushort expectedPackedValue1 = ushort.MaxValue; ushort expectedPackedValue2 = 0xFC1F; @@ -151,8 +153,8 @@ public class Bgra5551Tests public void Bgra5551_FromBgra32() { // arrange - var bgra1 = default(Bgra5551); - var bgra2 = default(Bgra5551); + Bgra5551 bgra1 = default; + Bgra5551 bgra2 = default; ushort expectedPackedValue1 = ushort.MaxValue; ushort expectedPackedValue2 = 0xFC1F; @@ -169,7 +171,7 @@ public class Bgra5551Tests public void Bgra5551_FromArgb32() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -183,7 +185,7 @@ public class Bgra5551Tests public void Bgra5551_FromRgb48() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -197,7 +199,7 @@ public class Bgra5551Tests public void Bgra5551_FromRgba64() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -211,7 +213,7 @@ public class Bgra5551Tests public void Bgra5551_FromGrey16() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -225,7 +227,7 @@ public class Bgra5551Tests public void Bgra5551_FromGrey8() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -239,7 +241,7 @@ public class Bgra5551Tests public void Bgra5551_FromBgr24() { // arrange - var bgra = default(Bgra5551); + Bgra5551 bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -253,7 +255,8 @@ public class Bgra5551Tests public void Bgra5551_FromRgb24() { // arrange - var bgra = default(Bgra5551); + Bgra5551 + bgra = default; ushort expectedPackedValue = ushort.MaxValue; // act @@ -269,4 +272,14 @@ public class Bgra5551Tests Assert.Equal(Vector4.Zero, new Bgra5551(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Bgra5551(Vector4.One * 1234.0f).ToVector4()); } + + [Fact] + public void Bgra5551_PixelInformation() + { + PixelTypeInfo info = Bgra5551.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 435f07391..ce5752fba 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Byte4Tests [Fact] public void AreEqual() { - var color1 = new Byte4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Byte4(new Vector4(0.0f)); - var color3 = new Byte4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new Byte4(1.0f, 0.0f, 1.0f, 1.0f); + Byte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Byte4 color2 = new(new Vector4(0.0f)); + Byte4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + Byte4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Byte4Tests [Fact] public void AreNotEqual() { - var color1 = new Byte4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Byte4(new Vector4(1.0f)); - var color3 = new Byte4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Byte4(1.0f, 1.0f, 0.0f, 1.0f); + Byte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Byte4 color2 = new(new Vector4(1.0f)); + Byte4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Byte4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -63,7 +65,7 @@ public class Byte4Tests public void Byte4_ToScaledVector4() { // arrange - var byte4 = new Byte4(Vector4.One * 255); + Byte4 byte4 = new(Vector4.One * 255); // act Vector4 actual = byte4.ToScaledVector4(); @@ -79,9 +81,9 @@ public class Byte4Tests public void Byte4_ToRgba32() { // arrange - var byte4 = new Byte4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Byte4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act byte4.ToRgba32(ref actual); @@ -94,7 +96,7 @@ public class Byte4Tests { // arrange Vector4 scaled = new Byte4(Vector4.One * 255).ToScaledVector4(); - var pixel = default(Byte4); + Byte4 pixel = default; uint expected = 0xFFFFFFFF; // act @@ -109,7 +111,7 @@ public class Byte4Tests public void Byte4_FromArgb32() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -123,7 +125,7 @@ public class Byte4Tests public void Byte4_FromBgr24() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -137,7 +139,7 @@ public class Byte4Tests public void Byte4_FromGrey8() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -151,7 +153,7 @@ public class Byte4Tests public void Byte4_FromGrey16() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -165,7 +167,7 @@ public class Byte4Tests public void Byte4_FromRgb24() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -179,7 +181,7 @@ public class Byte4Tests public void Byte4_FromBgra5551() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expected = 0xFFFFFFFF; // act @@ -193,7 +195,7 @@ public class Byte4Tests public void Byte4_FromRgba32() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue1 = uint.MaxValue; // act @@ -207,7 +209,7 @@ public class Byte4Tests public void Byte4_FromRgb48() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -221,7 +223,7 @@ public class Byte4Tests public void Byte4_FromRgba64() { // arrange - var byte4 = default(Byte4); + Byte4 byte4 = default; uint expectedPackedValue = uint.MaxValue; // act @@ -237,4 +239,14 @@ public class Byte4Tests Assert.Equal(Vector4.Zero, new Byte4(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One * 255, new Byte4(Vector4.One * 1234.0f).ToVector4()); } + + [Fact] + public void Byte4_PixelInformation() + { + PixelTypeInfo info = Byte4.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index 9ffaf3e21..cbff2b85e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -25,11 +27,11 @@ public class HalfSingleTests public void HalfSingle_ToVector4() { // arrange - var halfSingle = new HalfSingle(0.5f); - var expected = new Vector4(0.5f, 0, 0, 1); + HalfSingle halfSingle = new(0.5f); + Vector4 expected = new(0.5f, 0, 0, 1); // act - var actual = halfSingle.ToVector4(); + Vector4 actual = halfSingle.ToVector4(); // assert Assert.Equal(expected, actual); @@ -39,7 +41,7 @@ public class HalfSingleTests public void HalfSingle_ToScaledVector4() { // arrange - var halfSingle = new HalfSingle(-1F); + HalfSingle halfSingle = new(-1F); // act Vector4 actual = halfSingle.ToScaledVector4(); @@ -57,7 +59,7 @@ public class HalfSingleTests // arrange Vector4 scaled = new HalfSingle(-1F).ToScaledVector4(); int expected = 48128; - var halfSingle = default(HalfSingle); + HalfSingle halfSingle = default; // act halfSingle.FromScaledVector4(scaled); @@ -66,4 +68,14 @@ public class HalfSingleTests // assert Assert.Equal(expected, actual); } + + [Fact] + public void HalfSingle_PixelInformation() + { + PixelTypeInfo info = HalfSingle.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(1, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index eda931536..98a5cb0bf 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -30,7 +32,7 @@ public class HalfVector2Tests public void HalfVector2_ToScaledVector4() { // arrange - var halfVector = new HalfVector2(Vector2.One); + HalfVector2 halfVector = new(Vector2.One); // act Vector4 actual = halfVector.ToScaledVector4(); @@ -48,7 +50,7 @@ public class HalfVector2Tests // arrange Vector4 scaled = new HalfVector2(Vector2.One).ToScaledVector4(); uint expected = 1006648320u; - var halfVector = default(HalfVector2); + HalfVector2 halfVector = default; // act halfVector.FromScaledVector4(scaled); @@ -62,11 +64,11 @@ public class HalfVector2Tests public void HalfVector2_ToVector4() { // arrange - var halfVector = new HalfVector2(.5F, .25F); - var expected = new Vector4(0.5f, .25F, 0, 1); + HalfVector2 halfVector = new(.5F, .25F); + Vector4 expected = new(0.5f, .25F, 0, 1); // act - var actual = halfVector.ToVector4(); + Vector4 actual = halfVector.ToVector4(); // assert Assert.Equal(expected, actual); @@ -76,7 +78,7 @@ public class HalfVector2Tests public void HalfVector2_FromBgra5551() { // arrange - var halfVector2 = default(HalfVector2); + HalfVector2 halfVector2 = default; // act halfVector2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -88,4 +90,14 @@ public class HalfVector2Tests Assert.Equal(0, actual.Z); Assert.Equal(1, actual.W); } + + [Fact] + public void HalfVector2_PixelInformation() + { + PixelTypeInfo info = HalfVector2.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index 77120192b..48d10f1c1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -38,7 +40,7 @@ public class HalfVector4Tests public void HalfVector4_ToScaledVector4() { // arrange - var halfVector4 = new HalfVector4(-Vector4.One); + HalfVector4 halfVector4 = new(-Vector4.One); // act Vector4 actual = halfVector4.ToScaledVector4(); @@ -54,7 +56,7 @@ public class HalfVector4Tests public void HalfVector4_FromScaledVector4() { // arrange - var halfVector4 = default(HalfVector4); + HalfVector4 halfVector4 = default; Vector4 scaled = new HalfVector4(-Vector4.One).ToScaledVector4(); ulong expected = 13547034390470638592uL; @@ -70,7 +72,7 @@ public class HalfVector4Tests public void HalfVector4_FromBgra5551() { // arrange - var halfVector4 = default(HalfVector4); + HalfVector4 halfVector4 = default; Vector4 expected = Vector4.One; // act @@ -79,4 +81,14 @@ public class HalfVector4Tests // assert Assert.Equal(expected, halfVector4.ToScaledVector4()); } + + [Fact] + public void HalfVector4_PixelInformation() + { + PixelTypeInfo info = HalfVector4.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 685da2ddf..18481712a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +14,8 @@ public class L16Tests [Fact] public void AreEqual() { - var color1 = new L16(3000); - var color2 = new L16(3000); + L16 color1 = new(3000); + L16 color2 = new(3000); Assert.Equal(color1, color2); } @@ -21,8 +23,8 @@ public class L16Tests [Fact] public void AreNotEqual() { - var color1 = new L16(12345); - var color2 = new L16(54321); + L16 color1 = new(12345); + L16 color2 = new(54321); Assert.NotEqual(color1, color2); } @@ -58,7 +60,7 @@ public class L16Tests public void L16_ToScaledVector4(ushort input) { // Arrange - var gray = new L16(input); + L16 gray = new(input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -77,7 +79,7 @@ public class L16Tests // Arrange L16 gray = default; const ushort expected = 32767; - var vector = new L16(expected).ToVector4(); + Vector4 vector = new L16(expected).ToVector4(); // Act gray.FromVector4(vector); @@ -94,10 +96,10 @@ public class L16Tests public void L16_ToVector4(ushort input) { // Arrange - var gray = new L16(input); + L16 gray = new(input); // Act - var actual = gray.ToVector4(); + Vector4 actual = gray.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -132,7 +134,7 @@ public class L16Tests { // Arrange ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); - var gray = new L16(input); + L16 gray = new(input); // Act Rgba32 actual = default; @@ -149,7 +151,7 @@ public class L16Tests public void L16_FromBgra5551() { // arrange - var gray = default(L16); + L16 gray = default; ushort expected = ushort.MaxValue; // act @@ -158,4 +160,14 @@ public class L16Tests // assert Assert.Equal(expected, gray.PackedValue); } + + [Fact] + public void L16_PixelInformation() + { + PixelTypeInfo info = L16.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(1, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index afdc039a2..80dc36fa3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -24,8 +26,8 @@ public class L8Tests [Fact] public void AreEqual() { - var color1 = new L8(100); - var color2 = new L8(100); + L8 color1 = new(100); + L8 color2 = new(100); Assert.Equal(color1, color2); } @@ -33,8 +35,8 @@ public class L8Tests [Fact] public void AreNotEqual() { - var color1 = new L8(100); - var color2 = new L8(200); + L8 color1 = new(100); + L8 color2 = new(200); Assert.NotEqual(color1, color2); } @@ -60,7 +62,7 @@ public class L8Tests public void L8_ToScaledVector4(byte input) { // Arrange - var gray = new L8(input); + L8 gray = new(input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -79,7 +81,7 @@ public class L8Tests { // Arrange L8 gray = default; - var vector = new L8(luminance).ToVector4(); + Vector4 vector = new L8(luminance).ToVector4(); // Act gray.FromVector4(vector); @@ -94,10 +96,10 @@ public class L8Tests public void L8_ToVector4(byte input) { // Arrange - var gray = new L8(input); + L8 gray = new(input); // Act - var actual = gray.ToVector4(); + Vector4 actual = gray.ToVector4(); // Assert float scaledInput = input / 255F; @@ -128,7 +130,7 @@ public class L8Tests public void L8_ToRgba32(byte luminance) { // Arrange - var gray = new L8(luminance); + L8 gray = new(luminance); // Act Rgba32 actual = default; @@ -145,7 +147,7 @@ public class L8Tests public void L8_FromBgra5551() { // arrange - var grey = default(L8); + L8 grey = default; byte expected = byte.MaxValue; // act @@ -164,7 +166,7 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void L8_FromRgba32_IsInverseOf_ToRgba32(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -179,7 +181,7 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void Rgba32_ToL8_IsInverseOf_L8_ToRgba32(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -194,13 +196,13 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void ToVector4_IsRgba32Compatible(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - var l8Vector = original.ToVector4(); - var rgbaVector = original.ToVector4(); + Vector4 l8Vector = original.ToVector4(); + Vector4 rgbaVector = original.ToVector4(); Assert.Equal(l8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -209,12 +211,12 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void FromVector4_IsRgba32Compatible(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - var rgbaVector = original.ToVector4(); + Vector4 rgbaVector = original.ToVector4(); L8 mirror = default; mirror.FromVector4(rgbaVector); @@ -226,7 +228,7 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void ToScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -241,7 +243,7 @@ public class L8Tests [MemberData(nameof(LuminanceData))] public void FromScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new L8(luminance); + L8 original = new(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -253,5 +255,15 @@ public class L8Tests Assert.Equal(original, mirror); } + + [Fact] + public void L8_PixelInformation() + { + PixelTypeInfo info = L8.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(1, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index 92b4a4e1a..c3d8a873a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -24,8 +26,8 @@ public class La16Tests [Fact] public void AreEqual() { - var color1 = new La16(100, 50); - var color2 = new La16(100, 50); + La16 color1 = new(100, 50); + La16 color2 = new(100, 50); Assert.Equal(color1, color2); } @@ -33,8 +35,8 @@ public class La16Tests [Fact] public void AreNotEqual() { - var color1 = new La16(100, 50); - var color2 = new La16(200, 50); + La16 color1 = new(100, 50); + La16 color2 = new(200, 50); Assert.NotEqual(color1, color2); } @@ -60,7 +62,7 @@ public class La16Tests public void La16_ToScaledVector4(byte input) { // Arrange - var gray = new La16(input, input); + La16 gray = new(input, input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -79,7 +81,7 @@ public class La16Tests { // Arrange La16 gray = default; - var vector = new La16(luminance, luminance).ToVector4(); + Vector4 vector = new La16(luminance, luminance).ToVector4(); // Act gray.FromVector4(vector); @@ -96,10 +98,10 @@ public class La16Tests public void La16_ToVector4(byte input) { // Arrange - var gray = new La16(input, input); + La16 gray = new(input, input); // Act - var actual = gray.ToVector4(); + Vector4 actual = gray.ToVector4(); // Assert float scaledInput = input / 255F; @@ -131,7 +133,7 @@ public class La16Tests public void La16_ToRgba32(byte luminance) { // Arrange - var gray = new La16(luminance, luminance); + La16 gray = new(luminance, luminance); // Act Rgba32 actual = default; @@ -148,7 +150,7 @@ public class La16Tests public void La16_FromBgra5551() { // arrange - var grey = default(La16); + La16 grey = default; byte expected = byte.MaxValue; // act @@ -168,7 +170,7 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void La16_FromRgba32_IsInverseOf_ToRgba32(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -183,7 +185,7 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void Rgba32_ToLa16_IsInverseOf_La16_ToRgba32(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -198,13 +200,13 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void ToVector4_IsRgba32Compatible(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - var la16Vector = original.ToVector4(); - var rgbaVector = original.ToVector4(); + Vector4 la16Vector = original.ToVector4(); + Vector4 rgbaVector = original.ToVector4(); Assert.Equal(la16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -213,12 +215,12 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void FromVector4_IsRgba32Compatible(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - var rgbaVector = original.ToVector4(); + Vector4 rgbaVector = original.ToVector4(); La16 mirror = default; mirror.FromVector4(rgbaVector); @@ -230,7 +232,7 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void ToScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -245,7 +247,7 @@ public class La16Tests [MemberData(nameof(LuminanceData))] public void FromScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new La16(luminance, luminance); + La16 original = new(luminance, luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); @@ -257,5 +259,15 @@ public class La16Tests Assert.Equal(original, mirror); } + + [Fact] + public void La16_PixelInformation() + { + PixelTypeInfo info = La16.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index d333818c7..17e10e15d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +14,8 @@ public class La32Tests [Fact] public void AreEqual() { - var color1 = new La32(3000, 100); - var color2 = new La32(3000, 100); + La32 color1 = new(3000, 100); + La32 color2 = new(3000, 100); Assert.Equal(color1, color2); } @@ -21,8 +23,8 @@ public class La32Tests [Fact] public void AreNotEqual() { - var color1 = new La32(12345, 100); - var color2 = new La32(54321, 100); + La32 color1 = new(12345, 100); + La32 color2 = new(54321, 100); Assert.NotEqual(color1, color2); } @@ -60,7 +62,7 @@ public class La32Tests public void La32_ToScaledVector4(ushort input) { // Arrange - var gray = new La32(input, input); + La32 gray = new(input, input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -79,7 +81,7 @@ public class La32Tests // Arrange La32 gray = default; const ushort expected = 32767; - var vector = new La32(expected, expected).ToVector4(); + Vector4 vector = new La32(expected, expected).ToVector4(); // Act gray.FromVector4(vector); @@ -98,10 +100,10 @@ public class La32Tests public void La32_ToVector4(ushort input) { // Arrange - var gray = new La32(input, input); + La32 gray = new(input, input); // Act - var actual = gray.ToVector4(); + Vector4 actual = gray.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -137,7 +139,7 @@ public class La32Tests { // Arrange ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); - var gray = new La32(input, ushort.MaxValue); + La32 gray = new(input, ushort.MaxValue); // Act Rgba32 actual = default; @@ -154,7 +156,7 @@ public class La32Tests public void La32_FromBgra5551() { // arrange - var gray = default(La32); + La32 gray = default; ushort expected = ushort.MaxValue; // act @@ -164,4 +166,14 @@ public class La32Tests Assert.Equal(expected, gray.L); Assert.Equal(expected, gray.A); } + + [Fact] + public void La32_PixelInformation() + { + PixelTypeInfo info = La32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 14c590d0b..a09454f5b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -39,7 +41,7 @@ public class NormalizedByte2Tests public void NormalizedByte2_ToScaledVector4() { // arrange - var byte2 = new NormalizedByte2(-Vector2.One); + NormalizedByte2 byte2 = new(-Vector2.One); // act Vector4 actual = byte2.ToScaledVector4(); @@ -56,7 +58,7 @@ public class NormalizedByte2Tests { // arrange Vector4 scaled = new NormalizedByte2(-Vector2.One).ToScaledVector4(); - var byte2 = default(NormalizedByte2); + NormalizedByte2 byte2 = default; uint expected = 0x8181; // act @@ -71,8 +73,8 @@ public class NormalizedByte2Tests public void NormalizedByte2_FromBgra5551() { // arrange - var normalizedByte2 = default(NormalizedByte2); - var expected = new Vector4(1, 1, 0, 1); + NormalizedByte2 normalizedByte2 = default; + Vector4 expected = new(1, 1, 0, 1); // act normalizedByte2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -80,4 +82,14 @@ public class NormalizedByte2Tests // assert Assert.Equal(expected, normalizedByte2.ToVector4()); } + + [Fact] + public void NormalizedByte2_PixelInformation() + { + PixelTypeInfo info = NormalizedByte2.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.SByte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index ca73a6c1f..aa0136b37 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class NormalizedByte4Tests [Fact] public void AreEqual() { - var color1 = new NormalizedByte4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new NormalizedByte4(new Vector4(0.0f)); - var color3 = new NormalizedByte4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new NormalizedByte4(1.0f, 0.0f, 1.0f, 1.0f); + NormalizedByte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + NormalizedByte4 color2 = new(new Vector4(0.0f)); + NormalizedByte4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + NormalizedByte4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class NormalizedByte4Tests [Fact] public void AreNotEqual() { - var color1 = new NormalizedByte4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new NormalizedByte4(new Vector4(1.0f)); - var color3 = new NormalizedByte4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new NormalizedByte4(1.0f, 1.0f, 0.0f, 1.0f); + NormalizedByte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + NormalizedByte4 color2 = new(new Vector4(1.0f)); + NormalizedByte4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + NormalizedByte4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -63,7 +65,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_ToScaledVector4() { // arrange - var short4 = new NormalizedByte4(-Vector4.One); + NormalizedByte4 short4 = new(-Vector4.One); // act Vector4 actual = short4.ToScaledVector4(); @@ -79,7 +81,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromScaledVector4() { // arrange - var pixel = default(NormalizedByte4); + NormalizedByte4 pixel = default; Vector4 scaled = new NormalizedByte4(-Vector4.One).ToScaledVector4(); uint expected = 0x81818181; @@ -95,7 +97,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromArgb32() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -109,7 +111,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromBgr24() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -123,7 +125,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromGrey8() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -137,7 +139,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromGrey16() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -151,7 +153,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromRgb24() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -165,7 +167,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromRgba32() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -179,7 +181,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromBgra5551() { // arrange - var normalizedByte4 = default(NormalizedByte4); + NormalizedByte4 normalizedByte4 = default; Vector4 expected = Vector4.One; // act @@ -193,7 +195,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromRgb48() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -207,7 +209,7 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromRgba64() { // arrange - var byte4 = default(NormalizedByte4); + NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -221,13 +223,23 @@ public class NormalizedByte4Tests public void NormalizedByte4_ToRgba32() { // arrange - var byte4 = new NormalizedByte4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + NormalizedByte4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act byte4.ToRgba32(ref actual); Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedByte4_PixelInformation() + { + PixelTypeInfo info = NormalizedByte4.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.SByte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index 70bfa1be7..6ac18fa08 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -43,7 +45,7 @@ public class NormalizedShort2Tests public void NormalizedShort2_ToScaledVector4() { // arrange - var short2 = new NormalizedShort2(-Vector2.One); + NormalizedShort2 short2 = new(-Vector2.One); // act Vector4 actual = short2.ToScaledVector4(); @@ -60,7 +62,7 @@ public class NormalizedShort2Tests { // arrange Vector4 scaled = new NormalizedShort2(-Vector2.One).ToScaledVector4(); - var short2 = default(NormalizedShort2); + NormalizedShort2 short2 = default; uint expected = 0x80018001; // act @@ -75,8 +77,8 @@ public class NormalizedShort2Tests public void NormalizedShort2_FromBgra5551() { // arrange - var normalizedShort2 = default(NormalizedShort2); - var expected = new Vector4(1, 1, 0, 1); + NormalizedShort2 normalizedShort2 = default; + Vector4 expected = new(1, 1, 0, 1); // act normalizedShort2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -84,4 +86,14 @@ public class NormalizedShort2Tests // assert Assert.Equal(expected, normalizedShort2.ToVector4()); } + + [Fact] + public void NormalizedShort2_PixelInformation() + { + PixelTypeInfo info = NormalizedShort2.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index 997c6df82..fcab7318b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class NormalizedShort4Tests [Fact] public void AreEqual() { - var color1 = new NormalizedShort4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new NormalizedShort4(new Vector4(0.0f)); - var color3 = new NormalizedShort4(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new NormalizedShort4(1.0f, 0.0f, 1.0f, 1.0f); + NormalizedShort4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + NormalizedShort4 color2 = new(new Vector4(0.0f)); + NormalizedShort4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + NormalizedShort4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class NormalizedShort4Tests [Fact] public void AreNotEqual() { - var color1 = new NormalizedShort4(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new NormalizedShort4(new Vector4(1.0f)); - var color3 = new NormalizedShort4(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new NormalizedShort4(1.0f, 1.0f, 0.0f, 1.0f); + NormalizedShort4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + NormalizedShort4 color2 = new(new Vector4(1.0f)); + NormalizedShort4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + NormalizedShort4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -64,7 +66,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_ToScaledVector4() { // arrange - var short4 = new NormalizedShort4(Vector4.One); + NormalizedShort4 short4 = new(Vector4.One); // act Vector4 actual = short4.ToScaledVector4(); @@ -80,7 +82,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromScaledVector4() { // arrange - var pixel = default(NormalizedShort4); + NormalizedShort4 pixel = default; Vector4 scaled = new NormalizedShort4(Vector4.One).ToScaledVector4(); ulong expected = 0x7FFF7FFF7FFF7FFF; @@ -96,7 +98,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromArgb32() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -110,7 +112,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromBgr24() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -124,7 +126,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromGrey8() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -138,7 +140,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromGrey16() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -152,7 +154,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromRgb24() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -166,7 +168,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromRgba32() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -180,7 +182,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromRgb48() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -194,7 +196,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromRgba64() { // arrange - var byte4 = default(NormalizedShort4); + NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act @@ -208,9 +210,9 @@ public class NormalizedShort4Tests public void NormalizedShort4_ToRgba32() { // arrange - var byte4 = new NormalizedShort4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + NormalizedShort4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); + Rgba32 actual = default; // act byte4.ToRgba32(ref actual); @@ -222,7 +224,7 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromBgra5551() { // arrange - var normalizedShort4 = default(NormalizedShort4); + NormalizedShort4 normalizedShort4 = default; Vector4 expected = Vector4.One; // act @@ -231,4 +233,14 @@ public class NormalizedShort4Tests // assert Assert.Equal(expected, normalizedShort4.ToVector4()); } + + [Fact] + public void NormalizedShort4_PixelInformation() + { + PixelTypeInfo info = NormalizedShort4.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index 2900b0d29..c5c534655 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ImageSharp.Formats; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -33,7 +35,7 @@ public class Rg32Tests public void Rg32_ToScaledVector4() { // arrange - var rg32 = new Rg32(Vector2.One); + Rg32 rg32 = new(Vector2.One); // act Vector4 actual = rg32.ToScaledVector4(); @@ -49,8 +51,8 @@ public class Rg32Tests public void Rg32_FromScaledVector4() { // arrange - var rg32 = new Rg32(Vector2.One); - var pixel = default(Rg32); + Rg32 rg32 = new(Vector2.One); + Rg32 pixel = default; uint expected = 0xFFFFFFFF; // act @@ -66,7 +68,7 @@ public class Rg32Tests public void Rg32_FromBgra5551() { // arrange - var rg32 = new Rg32(Vector2.One); + Rg32 rg32 = new(Vector2.One); uint expected = 0xFFFFFFFF; // act @@ -82,4 +84,14 @@ public class Rg32Tests Assert.Equal(Vector2.Zero, new Rg32(Vector2.One * -1234.0f).ToVector2()); Assert.Equal(Vector2.One, new Rg32(Vector2.One * 1234.0f).ToVector2()); } + + [Fact] + public void Rg32_PixelInformation() + { + PixelTypeInfo info = Rg32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index 2d1be8ab4..dbb2e8f03 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -21,7 +23,7 @@ public class Rgb24Tests [MemberData(nameof(ColorData))] public void Constructor(byte r, byte g, byte b) { - var p = new Rgb24(r, g, b); + Rgb24 p = new(r, g, b); Assert.Equal(r, p.R); Assert.Equal(g, p.G); @@ -31,7 +33,7 @@ public class Rgb24Tests [Fact] public unsafe void ByteLayoutIsSequentialRgb() { - var color = new Rgb24(1, 2, 3); + Rgb24 color = new(1, 2, 3); byte* ptr = (byte*)&color; Assert.Equal(1, ptr[0]); @@ -43,8 +45,8 @@ public class Rgb24Tests [MemberData(nameof(ColorData))] public void Equals_WhenTrue(byte r, byte g, byte b) { - var x = new Rgb24(r, g, b); - var y = new Rgb24(r, g, b); + Rgb24 x = new(r, g, b); + Rgb24 y = new(r, g, b); Assert.True(x.Equals(y)); Assert.True(x.Equals((object)y)); @@ -57,8 +59,8 @@ public class Rgb24Tests [InlineData(1, 255, 0, 0, 255, 0)] public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) { - var a = new Rgb24(r1, g1, b1); - var b = new Rgb24(r2, g2, b2); + Rgb24 a = new(r1, g1, b1); + Rgb24 b = new(r2, g2, b2); Assert.False(a.Equals(b)); Assert.False(a.Equals((object)b)); @@ -67,7 +69,7 @@ public class Rgb24Tests [Fact] public void FromRgba32() { - var rgb = default(Rgb24); + Rgb24 rgb = default; rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); @@ -84,7 +86,7 @@ public class Rgb24Tests [Fact] public void FromVector4() { - var rgb = default(Rgb24); + Rgb24 rgb = default; rgb.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); @@ -95,7 +97,7 @@ public class Rgb24Tests [Fact] public void ToVector4() { - var rgb = new Rgb24(1, 2, 3); + Rgb24 rgb = new(1, 2, 3); Assert.Equal(Vec(1, 2, 3), rgb.ToVector4()); } @@ -104,9 +106,9 @@ public class Rgb24Tests public void ToRgba32() { // arrange - var rgb = new Rgb24(1, 2, 3); + Rgb24 rgb = new(1, 2, 3); Rgba32 rgba = default; - var expected = new Rgba32(1, 2, 3, 255); + Rgba32 expected = new(1, 2, 3, 255); // act rgb.ToRgba32(ref rgba); @@ -119,7 +121,7 @@ public class Rgb24Tests public void Rgb24_FromBgra5551() { // arrange - var rgb = new Rgb24(255, 255, 255); + Rgb24 rgb = new(255, 255, 255); // act rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -129,4 +131,14 @@ public class Rgb24Tests Assert.Equal(255, rgb.G); Assert.Equal(255, rgb.B); } + + [Fact] + public void Rgb24_PixelInformation() + { + PixelTypeInfo info = Rgb24.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(3, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index d8a61940b..04bbaf7f7 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,7 +14,7 @@ public class Rgb48Tests [Fact] public void Rgb48_Values() { - var rgb = new Rgba64(5243, 9830, 19660, 29491); + Rgba64 rgb = new(5243, 9830, 19660, 29491); Assert.Equal(5243, rgb.R); Assert.Equal(9830, rgb.G); @@ -32,9 +34,9 @@ public class Rgb48Tests public void Rgb48_FromScaledVector4() { // arrange - var pixel = default(Rgb48); - var short3 = new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); - var expected = new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); + Rgb48 pixel = default; + Rgb48 short3 = new(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); + Rgb48 expected = new(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); // act Vector4 scaled = short3.ToScaledVector4(); @@ -48,8 +50,8 @@ public class Rgb48Tests public void Rgb48_ToRgba32() { // arrange - var rgba48 = new Rgb48(5140, 9766, 19532); - var expected = new Rgba32(20, 38, 76, 255); + Rgb48 rgba48 = new(5140, 9766, 19532); + Rgba32 expected = new(20, 38, 76, 255); // act Rgba32 actual = default; @@ -63,7 +65,7 @@ public class Rgb48Tests public void Rgb48_FromBgra5551() { // arrange - var rgb = default(Rgb48); + Rgb48 rgb = default; ushort expected = ushort.MaxValue; // act @@ -74,4 +76,14 @@ public class Rgb48Tests Assert.Equal(expected, rgb.G); Assert.Equal(expected, rgb.B); } + + [Fact] + public void Rgb48_PixelInformation() + { + PixelTypeInfo info = Rgb48.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(3, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 0c28b35c6..678ac2274 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +17,10 @@ public class Rgba1010102Tests [Fact] public void AreEqual() { - var color1 = new Rgba1010102(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Rgba1010102(new Vector4(0.0f)); - var color3 = new Rgba1010102(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - var color4 = new Rgba1010102(1.0f, 0.0f, 1.0f, 1.0f); + Rgba1010102 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Rgba1010102 color2 = new(new Vector4(0.0f)); + Rgba1010102 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); + Rgba1010102 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public class Rgba1010102Tests [Fact] public void AreNotEqual() { - var color1 = new Rgba1010102(0.0f, 0.0f, 0.0f, 0.0f); - var color2 = new Rgba1010102(new Vector4(1.0f)); - var color3 = new Rgba1010102(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - var color4 = new Rgba1010102(1.0f, 1.0f, 0.0f, 1.0f); + Rgba1010102 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); + Rgba1010102 color2 = new(new Vector4(1.0f)); + Rgba1010102 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + Rgba1010102 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -66,7 +68,7 @@ public class Rgba1010102Tests public void Rgba1010102_ToScaledVector4() { // arrange - var rgba = new Rgba1010102(Vector4.One); + Rgba1010102 rgba = new(Vector4.One); // act Vector4 actual = rgba.ToScaledVector4(); @@ -82,8 +84,8 @@ public class Rgba1010102Tests public void Rgba1010102_FromScaledVector4() { // arrange - var rgba = new Rgba1010102(Vector4.One); - var actual = default(Rgba1010102); + Rgba1010102 rgba = new(Vector4.One); + Rgba1010102 actual = default; uint expected = 0xFFFFFFFF; // act @@ -98,7 +100,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromBgra5551() { // arrange - var rgba = new Rgba1010102(Vector4.One); + Rgba1010102 rgba = new(Vector4.One); uint expected = 0xFFFFFFFF; // act @@ -112,7 +114,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromArgb32() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -126,8 +128,8 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgba32() { // arrange - var rgba1 = default(Rgba1010102); - var rgba2 = default(Rgba1010102); + Rgba1010102 rgba1 = default; + Rgba1010102 rgba2 = default; uint expectedPackedValue1 = uint.MaxValue; uint expectedPackedValue2 = 0xFFF003FF; @@ -144,7 +146,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromBgr24() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -158,7 +160,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromGrey8() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -172,7 +174,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromGrey16() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -186,7 +188,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgb24() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -200,7 +202,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgb48() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -214,7 +216,7 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgba64() { // arrange - var rgba = default(Rgba1010102); + Rgba1010102 rgba = default; uint expectedPackedValue = uint.MaxValue; // act @@ -235,8 +237,8 @@ public class Rgba1010102Tests public void Rgba1010102_ToRgba32() { // arrange - var rgba = new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f); - var expected = new Rgba32(25, 0, 128, 0); + Rgba1010102 rgba = new(0.1f, -0.3f, 0.5f, -0.7f); + Rgba32 expected = new(25, 0, 128, 0); // act Rgba32 actual = default; @@ -245,4 +247,14 @@ public class Rgba1010102Tests // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgba1010102_PixelInformation() + { + PixelTypeInfo info = Rgba1010102.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 64903f65b..6154773e8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -18,12 +20,12 @@ public class Rgba32Tests [Fact] public void AreEqual() { - var color1 = new Rgba32(0, 0, 0); - var color2 = new Rgba32(0, 0, 0, 1F); - var color3 = Rgba32.ParseHex("#000"); - var color4 = Rgba32.ParseHex("#000F"); - var color5 = Rgba32.ParseHex("#000000"); - var color6 = Rgba32.ParseHex("#000000FF"); + Rgba32 color1 = new(0, 0, 0); + Rgba32 color2 = new(0, 0, 0, 1F); + Rgba32 color3 = Rgba32.ParseHex("#000"); + Rgba32 color4 = Rgba32.ParseHex("#000F"); + Rgba32 color5 = Rgba32.ParseHex("#000000"); + Rgba32 color6 = Rgba32.ParseHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -38,11 +40,11 @@ public class Rgba32Tests [Fact] public void AreNotEqual() { - var color1 = new Rgba32(255, 0, 0, 255); - var color2 = new Rgba32(0, 0, 0, 255); - var color3 = Rgba32.ParseHex("#000"); - var color4 = Rgba32.ParseHex("#000000"); - var color5 = Rgba32.ParseHex("#FF000000"); + Rgba32 color1 = new(255, 0, 0, 255); + Rgba32 color2 = new(0, 0, 0, 255); + Rgba32 color3 = Rgba32.ParseHex("#000"); + Rgba32 color4 = Rgba32.ParseHex("#000000"); + Rgba32 color5 = Rgba32.ParseHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -56,25 +58,25 @@ public class Rgba32Tests [Fact] public void ConstructorAssignsProperties() { - var color1 = new Rgba32(1, .1f, .133f, .864f); + Rgba32 color1 = new(1, .1f, .133f, .864f); Assert.Equal(255, color1.R); Assert.Equal((byte)Math.Round(.1f * 255), color1.G); Assert.Equal((byte)Math.Round(.133f * 255), color1.B); Assert.Equal((byte)Math.Round(.864f * 255), color1.A); - var color2 = new Rgba32(1, .1f, .133f); + Rgba32 color2 = new(1, .1f, .133f); Assert.Equal(255, color2.R); Assert.Equal(Math.Round(.1f * 255), color2.G); Assert.Equal(Math.Round(.133f * 255), color2.B); Assert.Equal(255, color2.A); - var color4 = new Rgba32(new Vector3(1, .1f, .133f)); + Rgba32 color4 = new(new Vector3(1, .1f, .133f)); Assert.Equal(255, color4.R); Assert.Equal(Math.Round(.1f * 255), color4.G); Assert.Equal(Math.Round(.133f * 255), color4.B); Assert.Equal(255, color4.A); - var color5 = new Rgba32(new Vector4(1, .1f, .133f, .5f)); + Rgba32 color5 = new(new Vector4(1, .1f, .133f, .5f)); Assert.Equal(255, color5.R); Assert.Equal(Math.Round(.1f * 255), color5.G); Assert.Equal(Math.Round(.133f * 255), color5.B); @@ -88,7 +90,7 @@ public class Rgba32Tests public void FromAndToHex() { // 8 digit hex matches css4 spec. RRGGBBAA - var color = Rgba32.ParseHex("#AABBCCDD"); // 170, 187, 204, 221 + Rgba32 color = Rgba32.ParseHex("#AABBCCDD"); // 170, 187, 204, 221 Assert.Equal(170, color.R); Assert.Equal(187, color.G); Assert.Equal(204, color.B); @@ -111,7 +113,7 @@ public class Rgba32Tests [Fact] public unsafe void ByteLayout() { - var color = new Rgba32(1, 2, 3, 4); + Rgba32 color = new(1, 2, 3, 4); byte* colorBase = (byte*)&color; Assert.Equal(1, colorBase[0]); Assert.Equal(2, colorBase[1]); @@ -146,7 +148,7 @@ public class Rgba32Tests public void Rgba32_ToScaledVector4() { // arrange - var rgba = new Rgba32(Vector4.One); + Rgba32 rgba = new(Vector4.One); // act Vector4 actual = rgba.ToScaledVector4(); @@ -162,8 +164,8 @@ public class Rgba32Tests public void Rgba32_FromScaledVector4() { // arrange - var rgba = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Rgba32 rgba = new(Vector4.One); + Rgba32 actual = default; uint expected = 0xFFFFFFFF; // act @@ -185,9 +187,9 @@ public class Rgba32Tests public void Rgba32_ToRgba32() { // arrange - var rgba = new Rgba32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(0x1a, 0, 0x80, 0); + Rgba32 rgba = new(+0.1f, -0.3f, +0.5f, -0.7f); + Rgba32 actual = default; + Rgba32 expected = new(0x1a, 0, 0x80, 0); // act actual.FromRgba32(rgba); @@ -200,9 +202,9 @@ public class Rgba32Tests public void Rgba32_FromRgba32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Rgba32); - var expected = new Rgba32(0x1a, 0, 0x80, 0); + Rgba32 rgba = default; + Rgba32 actual = default; + Rgba32 expected = new(0x1a, 0, 0x80, 0); // act rgba.FromRgba32(expected); @@ -216,9 +218,9 @@ public class Rgba32Tests public void Rgba32_FromBgra32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Bgra32); - var expected = new Bgra32(0x1a, 0, 0x80, 0); + Rgba32 rgba = default; + Bgra32 actual = default; + Bgra32 expected = new(0x1a, 0, 0x80, 0); // act rgba.FromBgra32(expected); @@ -232,9 +234,9 @@ public class Rgba32Tests public void Rgba32_FromAbgr32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Abgr32); - var expected = new Abgr32(0x1a, 0, 0x80, 0); + Rgba32 rgba = default; + Abgr32 actual = default; + Abgr32 expected = new(0x1a, 0, 0x80, 0); // act rgba.FromAbgr32(expected); @@ -248,9 +250,9 @@ public class Rgba32Tests public void Rgba32_FromArgb32_ToArgb32() { // arrange - var rgba = default(Rgba32); - var actual = default(Argb32); - var expected = new Argb32(0x1a, 0, 0x80, 0); + Rgba32 rgba = default; + Argb32 actual = default; + Argb32 expected = new(0x1a, 0, 0x80, 0); // act rgba.FromArgb32(expected); @@ -264,9 +266,9 @@ public class Rgba32Tests public void Rgba32_FromRgb48() { // arrange - var input = default(Rgba32); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + Rgba32 input = default; + Rgb48 actual = default; + Rgb48 expected = new(65535, 0, 65535); // act input.FromRgb48(expected); @@ -280,9 +282,9 @@ public class Rgba32Tests public void Rgba32_FromRgba64() { // arrange - var input = default(Rgba32); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + Rgba32 input = default; + Rgba64 actual = default; + Rgba64 expected = new(65535, 0, 65535, 0); // act input.FromRgba64(expected); @@ -296,7 +298,7 @@ public class Rgba32Tests public void Rgba32_FromBgra5551() { // arrange - var rgb = default(Rgba32); + Rgba32 rgb = default; uint expected = 0xFFFFFFFF; // act @@ -305,4 +307,14 @@ public class Rgba32Tests // assert Assert.Equal(expected, rgb.PackedValue); } + + [Fact] + public void Rgba32_PixelInformation() + { + PixelTypeInfo info = Rgba32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index f60ed8a52..e9b61c1e3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -38,7 +40,7 @@ public class Rgba64Tests public void Rgba64_ToScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var short2 = new Rgba64(r, g, b, a); + Rgba64 short2 = new(r, g, b, a); float max = ushort.MaxValue; float rr = r / max; @@ -63,7 +65,7 @@ public class Rgba64Tests public void Rgba64_FromScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var source = new Rgba64(r, g, b, a); + Rgba64 source = new(r, g, b, a); // act Vector4 scaled = source.ToScaledVector4(); @@ -78,8 +80,8 @@ public class Rgba64Tests [Fact] public void Rgba64_Clamping() { - var zero = default(Rgba64); - var one = default(Rgba64); + Rgba64 zero = default; + Rgba64 one = default; zero.FromVector4(Vector4.One * -1234.0f); one.FromVector4(Vector4.One * 1234.0f); Assert.Equal(Vector4.Zero, zero.ToVector4()); @@ -90,9 +92,9 @@ public class Rgba64Tests public void Rgba64_ToRgba32() { // arrange - var rgba64 = new Rgba64(5140, 9766, 19532, 29555); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 76, 115); + Rgba64 rgba64 = new(5140, 9766, 19532, 29555); + Rgba32 actual = default; + Rgba32 expected = new(20, 38, 76, 115); // act rgba64.ToRgba32(ref actual); @@ -105,7 +107,7 @@ public class Rgba64Tests public void Rgba64_FromBgra5551() { // arrange - var rgba = default(Rgba64); + Rgba64 rgba = default; ushort expected = ushort.MaxValue; // act @@ -121,8 +123,8 @@ public class Rgba64Tests [Fact] public void Equality_WhenTrue() { - var c1 = new Rgba64(100, 2000, 3000, 40000); - var c2 = new Rgba64(100, 2000, 3000, 40000); + Rgba64 c1 = new(100, 2000, 3000, 40000); + Rgba64 c2 = new(100, 2000, 3000, 40000); Assert.True(c1.Equals(c2)); Assert.True(c1.GetHashCode() == c2.GetHashCode()); @@ -131,9 +133,9 @@ public class Rgba64Tests [Fact] public void Equality_WhenFalse() { - var c1 = new Rgba64(100, 2000, 3000, 40000); - var c2 = new Rgba64(101, 2000, 3000, 40000); - var c3 = new Rgba64(100, 2000, 3000, 40001); + Rgba64 c1 = new(100, 2000, 3000, 40000); + Rgba64 c2 = new(101, 2000, 3000, 40000); + Rgba64 c3 = new(100, 2000, 3000, 40001); Assert.False(c1.Equals(c2)); Assert.False(c2.Equals(c3)); @@ -143,8 +145,8 @@ public class Rgba64Tests [Fact] public void Rgba64_FromRgba32() { - var source = new Rgba32(20, 38, 76, 115); - var expected = new Rgba64(5140, 9766, 19532, 29555); + Rgba32 source = new(20, 38, 76, 115); + Rgba64 expected = new(5140, 9766, 19532, 29555); Rgba64 actual = default; actual.FromRgba32(source); @@ -155,9 +157,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Rgba32() { - var expected = new Rgba64(5140, 9766, 19532, 29555); - var source = new Rgba32(20, 38, 76, 115); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, 29555); + Rgba32 source = new(20, 38, 76, 115); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -165,9 +167,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Bgra32() { - var expected = new Rgba64(5140, 9766, 19532, 29555); - var source = new Bgra32(20, 38, 76, 115); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, 29555); + Bgra32 source = new(20, 38, 76, 115); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -175,9 +177,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Argb32() { - var expected = new Rgba64(5140, 9766, 19532, 29555); - var source = new Argb32(20, 38, 76, 115); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, 29555); + Argb32 source = new(20, 38, 76, 115); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -185,9 +187,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Abgr32() { - var expected = new Rgba64(5140, 9766, 19532, 29555); - var source = new Abgr32(20, 38, 76, 115); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, 29555); + Abgr32 source = new(20, 38, 76, 115); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -195,9 +197,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Rgb24() { - var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); - var source = new Rgb24(20, 38, 76); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, ushort.MaxValue); + Rgb24 source = new(20, 38, 76); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -205,9 +207,9 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Bgr24() { - var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); - var source = new Bgr24(20, 38, 76); - var actual = new Rgba64(source); + Rgba64 expected = new(5140, 9766, 19532, ushort.MaxValue); + Bgr24 source = new(20, 38, 76); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -215,11 +217,11 @@ public class Rgba64Tests [Fact] public void ConstructFrom_Vector4() { - var source = new Vector4(0f, 0.2f, 0.5f, 1f); + Vector4 source = new(0f, 0.2f, 0.5f, 1f); Rgba64 expected = default; expected.FromScaledVector4(source); - var actual = new Rgba64(source); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -228,11 +230,11 @@ public class Rgba64Tests public void ToRgba32_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Rgba32(20, 38, 76, 115); + Rgba64 source = new(5140, 9766, 19532, 29555); + Rgba32 expected = new(20, 38, 76, 115); // act - var actual = source.ToRgba32(); + Rgba32 actual = source.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -242,11 +244,11 @@ public class Rgba64Tests public void ToBgra32_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Bgra32(20, 38, 76, 115); + Rgba64 source = new(5140, 9766, 19532, 29555); + Bgra32 expected = new(20, 38, 76, 115); // act - var actual = source.ToBgra32(); + Bgra32 actual = source.ToBgra32(); // assert Assert.Equal(expected, actual); @@ -256,11 +258,11 @@ public class Rgba64Tests public void ToArgb32_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Argb32(20, 38, 76, 115); + Rgba64 source = new(5140, 9766, 19532, 29555); + Argb32 expected = new(20, 38, 76, 115); // act - var actual = source.ToArgb32(); + Argb32 actual = source.ToArgb32(); // assert Assert.Equal(expected, actual); @@ -270,11 +272,11 @@ public class Rgba64Tests public void ToAbgr32_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Abgr32(20, 38, 76, 115); + Rgba64 source = new(5140, 9766, 19532, 29555); + Abgr32 expected = new(20, 38, 76, 115); // act - var actual = source.ToAbgr32(); + Abgr32 actual = source.ToAbgr32(); // assert Assert.Equal(expected, actual); @@ -284,11 +286,11 @@ public class Rgba64Tests public void ToRgb24_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Rgb24(20, 38, 76); + Rgba64 source = new(5140, 9766, 19532, 29555); + Rgb24 expected = new(20, 38, 76); // act - var actual = source.ToRgb24(); + Rgb24 actual = source.ToRgb24(); // assert Assert.Equal(expected, actual); @@ -298,13 +300,23 @@ public class Rgba64Tests public void ToBgr24_Retval() { // arrange - var source = new Rgba64(5140, 9766, 19532, 29555); - var expected = new Bgr24(20, 38, 76); + Rgba64 source = new(5140, 9766, 19532, 29555); + Bgr24 expected = new(20, 38, 76); // act - var actual = source.ToBgr24(); + Bgr24 actual = source.ToBgr24(); // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgba64_PixelInformation() + { + PixelTypeInfo info = Rgba64.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index ac6d2e3ca..ba30b0bfe 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -19,12 +20,12 @@ public class RgbaVectorTests [Fact] public void AreEqual() { - var color1 = new RgbaVector(0, 0, 0F); - var color2 = new RgbaVector(0, 0, 0, 1F); - var color3 = RgbaVector.FromHex("#000"); - var color4 = RgbaVector.FromHex("#000F"); - var color5 = RgbaVector.FromHex("#000000"); - var color6 = RgbaVector.FromHex("#000000FF"); + RgbaVector color1 = new(0, 0, 0F); + RgbaVector color2 = new(0, 0, 0, 1F); + RgbaVector color3 = RgbaVector.FromHex("#000"); + RgbaVector color4 = RgbaVector.FromHex("#000F"); + RgbaVector color5 = RgbaVector.FromHex("#000000"); + RgbaVector color6 = RgbaVector.FromHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -39,11 +40,11 @@ public class RgbaVectorTests [Fact] public void AreNotEqual() { - var color1 = new RgbaVector(1, 0, 0, 1); - var color2 = new RgbaVector(0, 0, 0, 1); - var color3 = RgbaVector.FromHex("#000"); - var color4 = RgbaVector.FromHex("#000000"); - var color5 = RgbaVector.FromHex("#FF000000"); + RgbaVector color1 = new(1, 0, 0, 1); + RgbaVector color2 = new(0, 0, 0, 1); + RgbaVector color3 = RgbaVector.FromHex("#000"); + RgbaVector color4 = RgbaVector.FromHex("#000000"); + RgbaVector color5 = RgbaVector.FromHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -57,13 +58,13 @@ public class RgbaVectorTests [Fact] public void ConstructorAssignsProperties() { - var color1 = new RgbaVector(1, .1F, .133F, .864F); + RgbaVector color1 = new(1, .1F, .133F, .864F); Assert.Equal(1F, color1.R); Assert.Equal(.1F, color1.G); Assert.Equal(.133F, color1.B); Assert.Equal(.864F, color1.A); - var color2 = new RgbaVector(1, .1f, .133f); + RgbaVector color2 = new(1, .1f, .133f); Assert.Equal(1F, color2.R); Assert.Equal(.1F, color2.G); Assert.Equal(.133F, color2.B); @@ -76,7 +77,7 @@ public class RgbaVectorTests [Fact] public void FromAndToHex() { - var color = RgbaVector.FromHex("#AABBCCDD"); + RgbaVector color = RgbaVector.FromHex("#AABBCCDD"); Assert.Equal(170 / 255F, color.R); Assert.Equal(187 / 255F, color.G); Assert.Equal(204 / 255F, color.B); @@ -104,7 +105,7 @@ public class RgbaVectorTests [Fact] public void FloatLayout() { - var color = new RgbaVector(1F, 2, 3, 4); + RgbaVector color = new(1F, 2, 3, 4); Vector4 colorBase = Unsafe.As(ref Unsafe.Add(ref color, 0)); float[] ordered = new float[4]; colorBase.CopyTo(ordered); @@ -119,9 +120,9 @@ public class RgbaVectorTests public void RgbaVector_FromRgb48() { // arrange - var input = default(RgbaVector); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + RgbaVector input = default; + Rgb48 actual = default; + Rgb48 expected = new(65535, 0, 65535); // act input.FromRgb48(expected); @@ -135,9 +136,9 @@ public class RgbaVectorTests public void RgbaVector_FromRgba64() { // arrange - var input = default(RgbaVector); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + RgbaVector input = default; + Rgba64 actual = default; + Rgba64 expected = new(65535, 0, 65535, 0); // act input.FromRgba64(expected); @@ -151,7 +152,7 @@ public class RgbaVectorTests public void RgbaVector_FromBgra5551() { // arrange - var rgb = default(RgbaVector); + RgbaVector rgb = default; Vector4 expected = Vector4.One; // act @@ -165,7 +166,7 @@ public class RgbaVectorTests public void RgbaVector_FromGrey16() { // arrange - var rgba = default(RgbaVector); + RgbaVector rgba = default; Vector4 expected = Vector4.One; // act @@ -179,7 +180,7 @@ public class RgbaVectorTests public void RgbaVector_FromGrey8() { // arrange - var rgba = default(RgbaVector); + RgbaVector rgba = default; Vector4 expected = Vector4.One; // act @@ -204,4 +205,14 @@ public class RgbaVectorTests Assert.Equal(srcColor, cloneColor); } + + [Fact] + public void RgbaVector_PixelInformation() + { + PixelTypeInfo info = RgbaVector.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Float, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index 8fc080d81..27631cbc7 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -51,7 +53,7 @@ public class Short2Tests public void Short2_ToScaledVector4() { // arrange - var short2 = new Short2(Vector2.One * 0x7FFF); + Short2 short2 = new(Vector2.One * 0x7FFF); // act Vector4 actual = short2.ToScaledVector4(); @@ -67,8 +69,8 @@ public class Short2Tests public void Short2_FromScaledVector4() { // arrange - var pixel = default(Short2); - var short2 = new Short2(Vector2.One * 0x7FFF); + Short2 pixel = default; + Short2 short2 = new(Vector2.One * 0x7FFF); const ulong expected = 0x7FFF7FFF; // act @@ -84,9 +86,9 @@ public class Short2Tests public void Short2_ToRgba32() { // arrange - var short2 = new Short2(127.5f, -5.3f); - var actual = default(Rgba32); - var expected = new Rgba32(128, 127, 0, 255); + Short2 short2 = new(127.5f, -5.3f); + Rgba32 actual = default; + Rgba32 expected = new(128, 127, 0, 255); // act short2.ToRgba32(ref actual); @@ -99,9 +101,9 @@ public class Short2Tests public void Short2_FromRgba32_ToRgba32() { // arrange - var short2 = default(Short2); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 0, 255); + Short2 short2 = default; + Rgba32 actual = default; + Rgba32 expected = new(20, 38, 0, 255); // act short2.FromRgba32(expected); @@ -115,9 +117,9 @@ public class Short2Tests public void Short2_FromRgb48() { // arrange - var input = default(Short2); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); + Short2 input = default; + Rgb48 actual = default; + Rgb48 expected = new(65535, 65535, 0); // act input.FromRgb48(expected); @@ -131,9 +133,9 @@ public class Short2Tests public void Short2_FromRgba64() { // arrange - var input = default(Short2); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); + Short2 input = default; + Rgba64 actual = default; + Rgba64 expected = new(65535, 65535, 0, 65535); // act input.FromRgba64(expected); @@ -147,7 +149,7 @@ public class Short2Tests public void Short2_FromBgra5551() { // arrange - var short2 = default(Short2); + Short2 short2 = default; // act short2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); @@ -159,4 +161,14 @@ public class Short2Tests Assert.Equal(0, actual.Z); Assert.Equal(1, actual.W); } + + [Fact] + public void Short2_PixelInformation() + { + PixelTypeInfo info = Short2.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(2, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index c42062703..8d853f059 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +14,8 @@ public class Short4Tests [Fact] public void Short4_PackedValues() { - var shortValue1 = new Short4(11547, 12653, 29623, 193); - var shortValue2 = new Short4(0.1f, -0.3f, 0.5f, -0.7f); + Short4 shortValue1 = new(11547, 12653, 29623, 193); + Short4 shortValue2 = new(0.1f, -0.3f, 0.5f, -0.7f); Assert.Equal(0x00c173b7316d2d1bUL, shortValue1.PackedValue); Assert.Equal(18446462598732840960, shortValue2.PackedValue); @@ -38,7 +40,7 @@ public class Short4Tests public void Short4_ToScaledVector4() { // arrange - var short4 = new Short4(Vector4.One * 0x7FFF); + Short4 short4 = new(Vector4.One * 0x7FFF); // act Vector4 actual = short4.ToScaledVector4(); @@ -54,12 +56,12 @@ public class Short4Tests public void Short4_FromScaledVector4() { // arrange - var short4 = new Short4(Vector4.One * 0x7FFF); + Short4 short4 = new(Vector4.One * 0x7FFF); Vector4 scaled = short4.ToScaledVector4(); const long expected = 0x7FFF7FFF7FFF7FFF; // act - var pixel = default(Short4); + Short4 pixel = default; pixel.FromScaledVector4(scaled); // assert @@ -70,12 +72,12 @@ public class Short4Tests public void Short4_Clamping() { // arrange - var short1 = new Short4(Vector4.One * 1234567.0f); - var short2 = new Short4(Vector4.One * -1234567.0f); + Short4 short1 = new(Vector4.One * 1234567.0f); + Short4 short2 = new(Vector4.One * -1234567.0f); // act - var vector1 = short1.ToVector4(); - var vector2 = short2.ToVector4(); + Vector4 vector1 = short1.ToVector4(); + Vector4 vector2 = short2.ToVector4(); // assert Assert.Equal(Vector4.One * 0x7FFF, vector1); @@ -86,9 +88,9 @@ public class Short4Tests public void Short4_ToRgba32() { // arrange - var shortValue = new Short4(11547, 12653, 29623, 193); - var actual = default(Rgba32); - var expected = new Rgba32(172, 177, 243, 128); + Short4 shortValue = new(11547, 12653, 29623, 193); + Rgba32 actual = default; + Rgba32 expected = new(172, 177, 243, 128); // act shortValue.ToRgba32(ref actual); @@ -101,9 +103,9 @@ public class Short4Tests public void Short4_FromRgba32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 0, 255); + Short4 short4 = default; + Rgba32 actual = default; + Rgba32 expected = new(20, 38, 0, 255); // act short4.FromRgba32(expected); @@ -117,9 +119,9 @@ public class Short4Tests public void Short4_FromBgra32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Bgra32); - var expected = new Bgra32(20, 38, 0, 255); + Short4 short4 = default; + Bgra32 actual = default; + Bgra32 expected = new(20, 38, 0, 255); // act short4.FromBgra32(expected); @@ -135,9 +137,9 @@ public class Short4Tests public void Short4_FromArgb32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Argb32); - var expected = new Argb32(20, 38, 0, 255); + Short4 short4 = default; + Argb32 actual = default; + Argb32 expected = new(20, 38, 0, 255); // act short4.FromArgb32(expected); @@ -153,9 +155,9 @@ public class Short4Tests public void Short4_FromAbgrb32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Abgr32); - var expected = new Abgr32(20, 38, 0, 255); + Short4 short4 = default; + Abgr32 actual = default; + Abgr32 expected = new(20, 38, 0, 255); // act short4.FromAbgr32(expected); @@ -171,9 +173,9 @@ public class Short4Tests public void Short4_FromRgb48_ToRgb48() { // arrange - var input = default(Short4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + Short4 input = default; + Rgb48 actual = default; + Rgb48 expected = new(65535, 0, 65535); // act input.FromRgb48(expected); @@ -187,9 +189,9 @@ public class Short4Tests public void Short4_FromRgba64_ToRgba64() { // arrange - var input = default(Short4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + Short4 input = default; + Rgba64 actual = default; + Rgba64 expected = new(65535, 0, 65535, 0); // act input.FromRgba64(expected); @@ -203,7 +205,7 @@ public class Short4Tests public void Short4_FromBgra5551() { // arrange - var short4 = default(Short4); + Short4 short4 = default; Vector4 expected = Vector4.One; // act @@ -212,4 +214,14 @@ public class Short4Tests // assert Assert.Equal(expected, short4.ToScaledVector4()); } + + [Fact] + public void Short4_PixelInformation() + { + PixelTypeInfo info = Short4.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(4, info.ComponentCount); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 5cc35b43e..75e3b693c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs @@ -12,8 +12,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(24, 48, 96, 192); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -24,8 +24,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_From_Floats_Produce_Equal_Scaled_Component_OutPut() { - var color = new Rgba32(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -36,8 +36,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() { - var color = new Rgba32(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -48,8 +48,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() { - var color = new Rgba32(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F); + Rgba32 color = new(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -60,8 +60,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() { - var color = Rgba32.ParseHex("183060C0"); - var colorVector = RgbaVector.FromHex("183060C0"); + Rgba32 color = Rgba32.ParseHex("183060C0"); + RgbaVector colorVector = RgbaVector.FromHex("183060C0"); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -72,8 +72,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_To_Vector4_Produce_Equal_OutPut() { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(24, 48, 96, 192); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.ToVector4(), colorVector.ToVector4()); } @@ -81,8 +81,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(24, 48, 96, 192); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Rgba32 rgba = default; Rgba32 rgbaVector = default; @@ -95,8 +95,8 @@ public class UnPackedPixelTests [Fact] public void Color_Types_To_Hex_Produce_Equal_OutPut() { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 color = new(24, 48, 96, 192); + RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); // 183060C0 Assert.Equal(color.ToHex(), colorVector.ToHex()); diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 465a21970..a4232a968 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -263,7 +263,7 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public struct TestPixelForAgnosticDecode : IPixel { - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); public PixelOperations CreatePixelOperations() => new(); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index 5fb3501e3..ccb149ca3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; @@ -85,7 +85,7 @@ public abstract partial class TestImageProvider : IXunitSerializable private static TPixel GetBottomRightColor() { TPixel bottomRightColor = default; - bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f)); + bottomRightColor.FromScaledVector4(new Vector4(1f, 0f, 1f, 0.5f)); return bottomRightColor; } } From 75b9d88a48a82f6b760f38a6e95d40170c83c937 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:18:07 +0000 Subject: [PATCH 029/220] Bump actions/setup-dotnet from 3 to 4 Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3 to 4. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 4 ++-- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7c7791347..75bcb8a25 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -89,14 +89,14 @@ jobs: - name: DotNet Setup if: ${{ matrix.options.sdk-preview != true }} - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x - name: DotNet Setup Preview if: ${{ matrix.options.sdk-preview == true }} - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 7624e86b6..62b6477ee 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -55,7 +55,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget- - name: DotNet Setup - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x From c7f18c8a6f18051ab3cb1b9fed90442a5530e062 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Dec 2023 19:28:34 +1000 Subject: [PATCH 030/220] Use info for color conversion --- src/ImageSharp/Color/Color.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 99458d58a..3af3e04f0 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp; @@ -124,7 +125,9 @@ public readonly partial struct Color : IEquatable { return new((L16)(object)pixel); } - else if (Unsafe.SizeOf() <= Unsafe.SizeOf()) + + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + if (info.ComponentPrecision <= PixelComponentPrecision.Byte) { Rgba32 p = default; pixel.ToRgba32(ref p); From 51193111a1549b37fe7ea57c7e707945f31b662a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Dec 2023 22:24:35 +1000 Subject: [PATCH 031/220] Fix handling gif encoding for global palettes - Main (#2615) * Handle dedup of local palette of 256 length * handle when foreground overhangs bottom of background * prevent potential overflow * reduce to a single par of clamping operations * correctly calculate Rect when negative target set * Handle global ani with 256 palette and no trans * Bump diff for windows --------- Co-authored-by: Scott Williams Co-authored-by: Scott Williams <166440+tocsoft@users.noreply.github.com> --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 19 ++++++++++++------- .../Formats/Gif/MetadataExtensions.cs | 3 +++ .../Formats/Png/PngEncoderTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 4 +++- .../Images/Input/Gif/global-256-no-trans.gif | 3 +++ 5 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Gif/global-256-no-trans.gif diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 9988848d1..1215768e4 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -103,7 +103,14 @@ internal sealed class GifEncoderCore : IImageEncoderInternals { // We avoid dithering by default to preserve the original colors. int transparencyIndex = GetTransparentIndex(quantized, frameMetadata); - this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null }, transparencyIndex); + if (transparencyIndex >= 0 || gifMetadata.GlobalColorTable.Value.Length < 256) + { + this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null }, transparencyIndex); + } + else + { + this.quantizer = KnownQuantizers.Octree; + } } else { @@ -198,19 +205,17 @@ internal sealed class GifEncoderCore : IImageEncoderInternals private static GifFrameMetadata GetGifFrameMetadata(ImageFrame frame, int transparencyIndex) where TPixel : unmanaged, IPixel { + GifFrameMetadata? metadata = null; if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) { - return (GifFrameMetadata)gif.DeepClone(); + metadata = (GifFrameMetadata)gif.DeepClone(); } - - GifFrameMetadata? metadata = null; - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) + else if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) { AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); metadata = GifFrameMetadata.FromAnimatedMetadata(ani); } - - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) + else if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) { AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); metadata = GifFrameMetadata.FromAnimatedMetadata(ani); diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index 42602f2c7..ad06462e7 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -82,6 +82,9 @@ public static partial class MetadataExtensions // has a local palette with 256 colors and is not transparent we should use 'Source'. bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); + // If the color table is global and frame has no transparency. Consider it 'Source' also. + blendSource |= source.ColorTableMode == GifColorTableMode.Global && !source.HasTransparency; + return new() { ColorTable = source.LocalColorTable, diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index c81b7eb6c..e70854b08 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -499,7 +499,7 @@ public partial class PngEncoderTests // TODO: Find a better way to compare. // The image has been visually checked but the quantization pattern used in the png encoder // means we cannot use an exact comparison nor replicate using the quantizing processor. - ImageComparer.TolerantPercentage(0.46f).VerifySimilarity(output, image); + ImageComparer.TolerantPercentage(0.613f).VerifySimilarity(output, image); GifMetadata gif = image.Metadata.GetGifMetadata(); PngMetadata png = output.Metadata.GetPngMetadata(); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index b62852902..8aa95d349 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -488,6 +488,7 @@ public static class TestImages public const string MixedDisposal = "Gif/mixed-disposal.gif"; public const string M4nb = "Gif/m4nb.gif"; public const string Bit18RGBCube = "Gif/18-bit_RGB_Cube.gif"; + public const string Global256NoTrans = "Gif/global-256-no-trans.gif"; // Test images from https://github.com/robert-ancell/pygif/tree/master/test-suite public const string ZeroSize = "Gif/image-zero-size.gif"; @@ -535,7 +536,8 @@ public static class TestImages Issues.Issue2450_B, Issues.BadDescriptorWidth, Issues.Issue1530, - Bit18RGBCube + Bit18RGBCube, + Global256NoTrans }; } diff --git a/tests/Images/Input/Gif/global-256-no-trans.gif b/tests/Images/Input/Gif/global-256-no-trans.gif new file mode 100644 index 000000000..1afa0d21d --- /dev/null +++ b/tests/Images/Input/Gif/global-256-no-trans.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce8ed23b4e21328886f5aa7579079123ff6401efdf65e162e565b056ffddab56 +size 669286 From d1c231bb85876ed40d569a5f29497fb5e68c5cda Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 12 Dec 2023 08:08:51 +1000 Subject: [PATCH 032/220] Rename property, use int. --- src/ImageSharp/Color/Color.cs | 2 +- src/ImageSharp/Formats/PixelTypeInfo.cs | 8 ++++---- src/ImageSharp/PixelFormats/PixelComponentPrecision.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/A8Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/L16Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/L8Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/La16Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/La32Tests.cs | 2 +- .../ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs | 2 +- .../ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs | 2 +- .../PixelFormats/NormalizedShort2Tests.cs | 2 +- .../PixelFormats/NormalizedShort4Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs | 2 +- 32 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 3af3e04f0..711009050 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -127,7 +127,7 @@ public readonly partial struct Color : IEquatable } PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.ComponentPrecision <= PixelComponentPrecision.Byte) + if (info.MaxComponentPrecision <= PixelComponentPrecision.Byte) { Rgba32 p = default; pixel.ToRgba32(ref p); diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index 0c2a4f4c8..170ffcfae 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -26,12 +26,12 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// /// Gets the count of the color components /// - public byte ComponentCount { get; init; } + public int ComponentCount { get; init; } /// - /// Gets the pixel component precision. + /// Gets the maximum precision of components within the pixel. /// - public PixelComponentPrecision? ComponentPrecision { get; init; } + public PixelComponentPrecision? MaxComponentPrecision { get; init; } /// /// Gets the pixel alpha transparency behavior. @@ -48,7 +48,7 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) { BitsPerPixel = Unsafe.SizeOf() * 8, ComponentCount = componentCount, - ComponentPrecision = componentPrecision, + MaxComponentPrecision = componentPrecision, AlphaRepresentation = pixelAlphaRepresentation }; } diff --git a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs index 8b1298657..3480ac76e 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Provides enumeration of the maximum precision of individual components within a pixel format. +/// Provides enumeration of the precision of individual components within a pixel format. /// public enum PixelComponentPrecision { diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index 008bc652c..518aa203d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -118,6 +118,6 @@ public class A8Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs index 3c185383c..dbe02e986 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -153,6 +153,6 @@ public class Abgr32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index f6b818fb5..fd8ee144b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -151,6 +151,6 @@ public class Argb32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 6861dab41..78c58e5c2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -136,6 +136,6 @@ public class Bgr24Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index b68b94325..68f953225 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -258,6 +258,6 @@ public class Bgr565Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 17d184d1a..e92fcf1d3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -153,6 +153,6 @@ public class Bgra32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index 648ef79d5..1af84c0c8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -254,6 +254,6 @@ public class Bgra4444Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index e32ec665e..a0926d4dd 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -280,6 +280,6 @@ public class Bgra5551Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index ce5752fba..5b456459e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -247,6 +247,6 @@ public class Byte4Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index cbff2b85e..9c546e2f0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -76,6 +76,6 @@ public class HalfSingleTests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index 98a5cb0bf..4f84d6087 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -98,6 +98,6 @@ public class HalfVector2Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index 48d10f1c1..33f0173d2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -89,6 +89,6 @@ public class HalfVector4Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 18481712a..0c0cdbe3b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -168,6 +168,6 @@ public class L16Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index 80dc36fa3..d8bce0640 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -263,7 +263,7 @@ public class L8Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index c3d8a873a..46e7457a5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -267,7 +267,7 @@ public class La16Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index 17e10e15d..65aa8b1c9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -174,6 +174,6 @@ public class La32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index a09454f5b..999ddb934 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -90,6 +90,6 @@ public class NormalizedByte2Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.SByte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.SByte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index aa0136b37..64a996453 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -240,6 +240,6 @@ public class NormalizedByte4Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.SByte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.SByte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index 6ac18fa08..e6d4ae98d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -94,6 +94,6 @@ public class NormalizedShort2Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index fcab7318b..cd684311a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -241,6 +241,6 @@ public class NormalizedShort4Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index c5c534655..bf3ff6416 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -92,6 +92,6 @@ public class Rg32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index dbb2e8f03..c69c757b5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -139,6 +139,6 @@ public class Rgb24Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index 04bbaf7f7..dda5af9fb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -84,6 +84,6 @@ public class Rgb48Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 678ac2274..5b0e4c287 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -255,6 +255,6 @@ public class Rgba1010102Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 6154773e8..d1ad4c304 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -315,6 +315,6 @@ public class Rgba32Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index e9b61c1e3..3dc580460 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -317,6 +317,6 @@ public class Rgba64Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index ba30b0bfe..968e274e2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -213,6 +213,6 @@ public class RgbaVectorTests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Float, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Float, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index 27631cbc7..7afe61b02 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -169,6 +169,6 @@ public class Short2Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index 8d853f059..bdde5cd8f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -222,6 +222,6 @@ public class Short4Tests Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.ComponentPrecision); + Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); } } From 768c1cbe7a65841b7c3f991cc54b3e478912e595 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:02:28 -0500 Subject: [PATCH 033/220] Update WebpFormat.cs Adjusted casing of Webp format name. --- src/ImageSharp/Formats/Webp/WebpFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/WebpFormat.cs b/src/ImageSharp/Formats/Webp/WebpFormat.cs index 197041234..1764182c5 100644 --- a/src/ImageSharp/Formats/Webp/WebpFormat.cs +++ b/src/ImageSharp/Formats/Webp/WebpFormat.cs @@ -18,7 +18,7 @@ public sealed class WebpFormat : IImageFormat public static WebpFormat Instance { get; } = new WebpFormat(); /// - public string Name => "Webp"; + public string Name => "WEBP"; /// public string DefaultMimeType => "image/webp"; From 715297c905f5e8a3199f157f67d0359a4f8ab6ad Mon Sep 17 00:00:00 2001 From: Mark Lagendijk Date: Mon, 18 Dec 2023 09:49:12 +0100 Subject: [PATCH 034/220] Fix WebP animation speed bug `Milliseconds` is the milli seconds component of the TimeSpan, `TotalMilliseconds` is the entire TimeSpan expressed as milliseconds. Example, Timespan of 3.5 seconds: * `Milliseconds`: 500 * `TotalMilliseconds`: 3500 --- src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs index 422ad6bc7..cd1b5d590 100644 --- a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs @@ -48,7 +48,7 @@ public class WebpFrameMetadata : IDeepCloneable internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) => new() { - FrameDelay = (uint)metadata.Duration.Milliseconds, + FrameDelay = (uint)metadata.Duration.TotalMilliseconds, BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over, DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose }; From 51cd84eb2f2354544eacdc8a2db3748daebe2fa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:05:53 +0000 Subject: [PATCH 035/220] Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 2 +- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 75bcb8a25..51765be6e 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -132,7 +132,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 62b6477ee..53eef4504 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -74,7 +74,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip From 6b6b474892d1a4ecfc450c3b61323d11a68145ab Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 2 Jan 2024 22:05:58 +1000 Subject: [PATCH 036/220] Introduce PixelComponentInfo + simplify Color --- src/ImageSharp/Color/Color.Conversions.cs | 240 --------------- src/ImageSharp/Color/Color.NamedColors.cs | 288 +++++++++--------- src/ImageSharp/Color/Color.cs | 145 ++++----- src/ImageSharp/Formats/AnimationUtilities.cs | 4 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 10 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 9 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 4 +- .../Formats/Png/PngScanlineProcessor.cs | 2 +- .../BlackIsZero1TiffColor{TPixel}.cs | 4 +- .../WhiteIsZero1TiffColor{TPixel}.cs | 4 +- .../Formats/Webp/BitWriter/BitWriterBase.cs | 3 +- .../Formats/Webp/WebpAnimationDecoder.cs | 2 +- .../Formats/Webp/WebpDecoderCore.cs | 2 +- src/ImageSharp/ImageInfo.cs | 2 +- src/ImageSharp/Image{TPixel}.cs | 1 - src/ImageSharp/PixelFormats/IPixel.cs | 1 - .../PixelFormats/PixelComponentInfo.cs | 111 +++++++ .../PixelFormats/PixelComponentPrecision.cs | 38 ++- .../PixelFormats/PixelImplementations/A8.cs | 3 +- .../PixelImplementations/Abgr32.cs | 19 +- .../PixelImplementations/Argb32.cs | 19 +- .../PixelImplementations/Bgr24.cs | 19 +- .../PixelImplementations/Bgr565.cs | 3 +- .../PixelImplementations/Bgra32.cs | 19 +- .../PixelImplementations/Bgra4444.cs | 3 +- .../PixelImplementations/Bgra5551.cs | 3 +- .../PixelImplementations/Byte4.cs | 3 +- .../PixelImplementations/HalfSingle.cs | 3 +- .../PixelImplementations/HalfVector2.cs | 3 +- .../PixelImplementations/HalfVector4.cs | 3 +- .../PixelFormats/PixelImplementations/L16.cs | 3 +- .../PixelFormats/PixelImplementations/L8.cs | 3 +- .../PixelFormats/PixelImplementations/La16.cs | 3 +- .../PixelFormats/PixelImplementations/La32.cs | 3 +- .../PixelImplementations/NormalizedByte2.cs | 3 +- .../PixelImplementations/NormalizedByte4.cs | 3 +- .../PixelImplementations/NormalizedShort2.cs | 3 +- .../PixelImplementations/NormalizedShort4.cs | 3 +- .../PixelFormats/PixelImplementations/Rg32.cs | 3 +- .../PixelImplementations/Rgb24.cs | 19 +- .../PixelImplementations/Rgb48.cs | 3 +- .../PixelImplementations/Rgba1010102.cs | 3 +- .../PixelImplementations/Rgba32.cs | 19 +- .../PixelImplementations/Rgba64.cs | 19 +- .../PixelImplementations/RgbaVector.cs | 3 +- .../PixelImplementations/Short2.cs | 3 +- .../PixelImplementations/Short4.cs | 3 +- .../PixelFormats/PixelOperations{TPixel}.cs | 1 - .../PixelTypeInfo.cs | 29 +- .../Filters/LomographProcessor{TPixel}.cs | 2 +- .../Filters/PolaroidProcessor{TPixel}.cs | 4 +- .../General/GetSetPixel.cs | 2 +- .../Processing/BokehBlur.cs | 2 +- .../Processing/Diffuse.cs | 4 +- .../Processing/GaussianBlur.cs | 2 +- .../Processing/Rotate.cs | 2 +- .../ImageSharp.Benchmarks/Processing/Skew.cs | 2 +- .../Color/ColorTests.CastFrom.cs | 28 +- .../Color/ColorTests.CastTo.cs | 32 +- .../Color/ColorTests.ConstructFrom.cs | 28 +- tests/ImageSharp.Tests/Color/ColorTests.cs | 128 +++----- tests/ImageSharp.Tests/Color/RgbaDouble.cs | 180 +++++++++++ .../Drawing/DrawImageTests.cs | 4 +- .../Formats/Png/PngDecoderTests.cs | 4 +- .../Formats/Png/PngEncoderTests.cs | 7 +- .../PhotometricInterpretationTestBase.cs | 2 +- .../Tiff/TiffEncoderMultiframeTests.cs | 20 +- .../ImageFrameCollectionTests.Generic.cs | 2 +- .../ImageFrameCollectionTests.NonGeneric.cs | 13 +- .../ImageSharp.Tests/Image/ImageFrameTests.cs | 4 +- .../Image/ImageTests.LoadPixelData.cs | 49 ++- .../Image/ImageTests.WrapMemory.cs | 16 +- tests/ImageSharp.Tests/Image/ImageTests.cs | 6 +- tests/ImageSharp.Tests/ImageInfoTests.cs | 2 +- .../ImageSharp.Tests/PixelFormats/A8Tests.cs | 9 +- .../PixelFormats/Abgr32Tests.cs | 12 +- .../PixelFormats/Argb32Tests.cs | 12 +- .../PixelFormats/Bgr24Tests.cs | 11 +- .../PixelFormats/Bgr565Tests.cs | 11 +- .../PixelFormats/Bgra32Tests.cs | 12 +- .../PixelFormats/Bgra4444Tests.cs | 12 +- .../PixelFormats/Bgra5551Tests.cs | 12 +- .../PixelFormats/Byte4Tests.cs | 12 +- .../PixelFormats/HalfSingleTests.cs | 9 +- .../PixelFormats/HalfVector2Tests.cs | 10 +- .../PixelFormats/HalfVector4Tests.cs | 12 +- .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 9 +- .../ImageSharp.Tests/PixelFormats/L8Tests.cs | 9 +- .../PixelFormats/La16Tests.cs | 10 +- .../PixelFormats/La32Tests.cs | 10 +- .../PixelFormats/NormalizedByte2Tests.cs | 10 +- .../PixelFormats/NormalizedByte4Tests.cs | 12 +- .../PixelFormats/NormalizedShort2Tests.cs | 10 +- .../PixelFormats/NormalizedShort4Tests.cs | 12 +- .../PixelFormats/PixelBlenderTests.cs | 42 +-- .../PixelFormats/Rg32Tests.cs | 10 +- .../PixelFormats/Rgb24Tests.cs | 11 +- .../PixelFormats/Rgb48Tests.cs | 11 +- .../PixelFormats/Rgba1010102Tests.cs | 12 +- .../PixelFormats/Rgba32Tests.cs | 12 +- .../PixelFormats/Rgba64Tests.cs | 12 +- .../PixelFormats/RgbaVectorTests.cs | 12 +- .../PixelFormats/Short2Tests.cs | 10 +- .../PixelFormats/Short4Tests.cs | 12 +- .../Quantization/WuQuantizerTests.cs | 4 +- tests/ImageSharp.Tests/TestFormat.cs | 2 +- .../ImageProviders/SolidProvider.cs | 2 +- 107 files changed, 1021 insertions(+), 991 deletions(-) delete mode 100644 src/ImageSharp/Color/Color.Conversions.cs create mode 100644 src/ImageSharp/PixelFormats/PixelComponentInfo.cs rename src/ImageSharp/{Formats => PixelFormats}/PixelTypeInfo.cs (65%) create mode 100644 tests/ImageSharp.Tests/Color/RgbaDouble.cs diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs deleted file mode 100644 index 309ab83ec..000000000 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp; - -/// -/// Contains constructors and implicit conversion methods. -/// -public readonly partial struct Color -{ - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Rgba64 pixel) - { - this.data = pixel; - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Rgb48 pixel) - { - this.data = new Rgba64(pixel.R, pixel.G, pixel.B, ushort.MaxValue); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(La32 pixel) - { - this.data = new Rgba64(pixel.L, pixel.L, pixel.L, pixel.A); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(L16 pixel) - { - this.data = new Rgba64(pixel.PackedValue, pixel.PackedValue, pixel.PackedValue, ushort.MaxValue); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Rgba32 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Argb32 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Bgra32 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Abgr32 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Rgb24 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Bgr24 pixel) - { - this.data = new Rgba64(pixel); - this.boxedHighPrecisionPixel = null; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] - public Color(Vector4 vector) - { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); - this.boxedHighPrecisionPixel = new RgbaVector(vector.X, vector.Y, vector.Z, vector.W); - this.data = default; - } - - /// - /// Converts a to . - /// - /// The . - /// The . - public static explicit operator Vector4(Color color) => color.ToScaledVector4(); - - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static explicit operator Color(Vector4 source) => new(source); - - [MethodImpl(InliningOptions.ShortMethod)] - internal Rgba32 ToRgba32() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToRgba32(); - } - - Rgba32 value = default; - this.boxedHighPrecisionPixel.ToRgba32(ref value); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Bgra32 ToBgra32() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToBgra32(); - } - - Bgra32 value = default; - value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Argb32 ToArgb32() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToArgb32(); - } - - Argb32 value = default; - value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Abgr32 ToAbgr32() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToAbgr32(); - } - - Abgr32 value = default; - value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Rgb24 ToRgb24() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToRgb24(); - } - - Rgb24 value = default; - value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Bgr24 ToBgr24() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToBgr24(); - } - - Bgr24 value = default; - value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return value; - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal Vector4 ToScaledVector4() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data.ToScaledVector4(); - } - - return this.boxedHighPrecisionPixel.ToScaledVector4(); - } -} diff --git a/src/ImageSharp/Color/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs index f8b4c90fd..00130dd90 100644 --- a/src/ImageSharp/Color/Color.NamedColors.cs +++ b/src/ImageSharp/Color/Color.NamedColors.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp; /// @@ -9,107 +11,107 @@ namespace SixLabors.ImageSharp; /// public readonly partial struct Color { - private static readonly Lazy> NamedColorsLookupLazy = new Lazy>(CreateNamedColorsLookup, true); + private static readonly Lazy> NamedColorsLookupLazy = new(CreateNamedColorsLookup, true); /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// - public static readonly Color AliceBlue = FromRgba(240, 248, 255, 255); + public static readonly Color AliceBlue = FromPixel(new Rgba32(240, 248, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FAEBD7. /// - public static readonly Color AntiqueWhite = FromRgba(250, 235, 215, 255); + public static readonly Color AntiqueWhite = FromPixel(new Rgba32(250, 235, 215, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly Color Aqua = FromRgba(0, 255, 255, 255); + public static readonly Color Aqua = FromPixel(new Rgba32(0, 255, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #7FFFD4. /// - public static readonly Color Aquamarine = FromRgba(127, 255, 212, 255); + public static readonly Color Aquamarine = FromPixel(new Rgba32(127, 255, 212, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F0FFFF. /// - public static readonly Color Azure = FromRgba(240, 255, 255, 255); + public static readonly Color Azure = FromPixel(new Rgba32(240, 255, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F5F5DC. /// - public static readonly Color Beige = FromRgba(245, 245, 220, 255); + public static readonly Color Beige = FromPixel(new Rgba32(245, 245, 220, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFE4C4. /// - public static readonly Color Bisque = FromRgba(255, 228, 196, 255); + public static readonly Color Bisque = FromPixel(new Rgba32(255, 228, 196, 255)); /// /// Represents a matching the W3C definition that has an hex value of #000000. /// - public static readonly Color Black = FromRgba(0, 0, 0, 255); + public static readonly Color Black = FromPixel(new Rgba32(0, 0, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFEBCD. /// - public static readonly Color BlanchedAlmond = FromRgba(255, 235, 205, 255); + public static readonly Color BlanchedAlmond = FromPixel(new Rgba32(255, 235, 205, 255)); /// /// Represents a matching the W3C definition that has an hex value of #0000FF. /// - public static readonly Color Blue = FromRgba(0, 0, 255, 255); + public static readonly Color Blue = FromPixel(new Rgba32(0, 0, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #8A2BE2. /// - public static readonly Color BlueViolet = FromRgba(138, 43, 226, 255); + public static readonly Color BlueViolet = FromPixel(new Rgba32(138, 43, 226, 255)); /// /// Represents a matching the W3C definition that has an hex value of #A52A2A. /// - public static readonly Color Brown = FromRgba(165, 42, 42, 255); + public static readonly Color Brown = FromPixel(new Rgba32(165, 42, 42, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DEB887. /// - public static readonly Color BurlyWood = FromRgba(222, 184, 135, 255); + public static readonly Color BurlyWood = FromPixel(new Rgba32(222, 184, 135, 255)); /// /// Represents a matching the W3C definition that has an hex value of #5F9EA0. /// - public static readonly Color CadetBlue = FromRgba(95, 158, 160, 255); + public static readonly Color CadetBlue = FromPixel(new Rgba32(95, 158, 160, 255)); /// /// Represents a matching the W3C definition that has an hex value of #7FFF00. /// - public static readonly Color Chartreuse = FromRgba(127, 255, 0, 255); + public static readonly Color Chartreuse = FromPixel(new Rgba32(127, 255, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #D2691E. /// - public static readonly Color Chocolate = FromRgba(210, 105, 30, 255); + public static readonly Color Chocolate = FromPixel(new Rgba32(210, 105, 30, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF7F50. /// - public static readonly Color Coral = FromRgba(255, 127, 80, 255); + public static readonly Color Coral = FromPixel(new Rgba32(255, 127, 80, 255)); /// /// Represents a matching the W3C definition that has an hex value of #6495ED. /// - public static readonly Color CornflowerBlue = FromRgba(100, 149, 237, 255); + public static readonly Color CornflowerBlue = FromPixel(new Rgba32(100, 149, 237, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFF8DC. /// - public static readonly Color Cornsilk = FromRgba(255, 248, 220, 255); + public static readonly Color Cornsilk = FromPixel(new Rgba32(255, 248, 220, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DC143C. /// - public static readonly Color Crimson = FromRgba(220, 20, 60, 255); + public static readonly Color Crimson = FromPixel(new Rgba32(220, 20, 60, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. @@ -119,27 +121,27 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #00008B. /// - public static readonly Color DarkBlue = FromRgba(0, 0, 139, 255); + public static readonly Color DarkBlue = FromPixel(new Rgba32(0, 0, 139, 255)); /// /// Represents a matching the W3C definition that has an hex value of #008B8B. /// - public static readonly Color DarkCyan = FromRgba(0, 139, 139, 255); + public static readonly Color DarkCyan = FromPixel(new Rgba32(0, 139, 139, 255)); /// /// Represents a matching the W3C definition that has an hex value of #B8860B. /// - public static readonly Color DarkGoldenrod = FromRgba(184, 134, 11, 255); + public static readonly Color DarkGoldenrod = FromPixel(new Rgba32(184, 134, 11, 255)); /// /// Represents a matching the W3C definition that has an hex value of #A9A9A9. /// - public static readonly Color DarkGray = FromRgba(169, 169, 169, 255); + public static readonly Color DarkGray = FromPixel(new Rgba32(169, 169, 169, 255)); /// /// Represents a matching the W3C definition that has an hex value of #006400. /// - public static readonly Color DarkGreen = FromRgba(0, 100, 0, 255); + public static readonly Color DarkGreen = FromPixel(new Rgba32(0, 100, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #A9A9A9. @@ -149,52 +151,52 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #BDB76B. /// - public static readonly Color DarkKhaki = FromRgba(189, 183, 107, 255); + public static readonly Color DarkKhaki = FromPixel(new Rgba32(189, 183, 107, 255)); /// /// Represents a matching the W3C definition that has an hex value of #8B008B. /// - public static readonly Color DarkMagenta = FromRgba(139, 0, 139, 255); + public static readonly Color DarkMagenta = FromPixel(new Rgba32(139, 0, 139, 255)); /// /// Represents a matching the W3C definition that has an hex value of #556B2F. /// - public static readonly Color DarkOliveGreen = FromRgba(85, 107, 47, 255); + public static readonly Color DarkOliveGreen = FromPixel(new Rgba32(85, 107, 47, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF8C00. /// - public static readonly Color DarkOrange = FromRgba(255, 140, 0, 255); + public static readonly Color DarkOrange = FromPixel(new Rgba32(255, 140, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #9932CC. /// - public static readonly Color DarkOrchid = FromRgba(153, 50, 204, 255); + public static readonly Color DarkOrchid = FromPixel(new Rgba32(153, 50, 204, 255)); /// /// Represents a matching the W3C definition that has an hex value of #8B0000. /// - public static readonly Color DarkRed = FromRgba(139, 0, 0, 255); + public static readonly Color DarkRed = FromPixel(new Rgba32(139, 0, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #E9967A. /// - public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); + public static readonly Color DarkSalmon = FromPixel(new Rgba32(233, 150, 122, 255)); /// /// Represents a matching the W3C definition that has an hex value of #8FBC8F. /// - public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255); + public static readonly Color DarkSeaGreen = FromPixel(new Rgba32(143, 188, 143, 255)); /// /// Represents a matching the W3C definition that has an hex value of #483D8B. /// - public static readonly Color DarkSlateBlue = FromRgba(72, 61, 139, 255); + public static readonly Color DarkSlateBlue = FromPixel(new Rgba32(72, 61, 139, 255)); /// /// Represents a matching the W3C definition that has an hex value of #2F4F4F. /// - public static readonly Color DarkSlateGray = FromRgba(47, 79, 79, 255); + public static readonly Color DarkSlateGray = FromPixel(new Rgba32(47, 79, 79, 255)); /// /// Represents a matching the W3C definition that has an hex value of #2F4F4F. @@ -204,27 +206,27 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #00CED1. /// - public static readonly Color DarkTurquoise = FromRgba(0, 206, 209, 255); + public static readonly Color DarkTurquoise = FromPixel(new Rgba32(0, 206, 209, 255)); /// /// Represents a matching the W3C definition that has an hex value of #9400D3. /// - public static readonly Color DarkViolet = FromRgba(148, 0, 211, 255); + public static readonly Color DarkViolet = FromPixel(new Rgba32(148, 0, 211, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF1493. /// - public static readonly Color DeepPink = FromRgba(255, 20, 147, 255); + public static readonly Color DeepPink = FromPixel(new Rgba32(255, 20, 147, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00BFFF. /// - public static readonly Color DeepSkyBlue = FromRgba(0, 191, 255, 255); + public static readonly Color DeepSkyBlue = FromPixel(new Rgba32(0, 191, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #696969. /// - public static readonly Color DimGray = FromRgba(105, 105, 105, 255); + public static readonly Color DimGray = FromPixel(new Rgba32(105, 105, 105, 255)); /// /// Represents a matching the W3C definition that has an hex value of #696969. @@ -234,62 +236,62 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #1E90FF. /// - public static readonly Color DodgerBlue = FromRgba(30, 144, 255, 255); + public static readonly Color DodgerBlue = FromPixel(new Rgba32(30, 144, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #B22222. /// - public static readonly Color Firebrick = FromRgba(178, 34, 34, 255); + public static readonly Color Firebrick = FromPixel(new Rgba32(178, 34, 34, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFAF0. /// - public static readonly Color FloralWhite = FromRgba(255, 250, 240, 255); + public static readonly Color FloralWhite = FromPixel(new Rgba32(255, 250, 240, 255)); /// /// Represents a matching the W3C definition that has an hex value of #228B22. /// - public static readonly Color ForestGreen = FromRgba(34, 139, 34, 255); + public static readonly Color ForestGreen = FromPixel(new Rgba32(34, 139, 34, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly Color Fuchsia = FromRgba(255, 0, 255, 255); + public static readonly Color Fuchsia = FromPixel(new Rgba32(255, 0, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DCDCDC. /// - public static readonly Color Gainsboro = FromRgba(220, 220, 220, 255); + public static readonly Color Gainsboro = FromPixel(new Rgba32(220, 220, 220, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F8F8FF. /// - public static readonly Color GhostWhite = FromRgba(248, 248, 255, 255); + public static readonly Color GhostWhite = FromPixel(new Rgba32(248, 248, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFD700. /// - public static readonly Color Gold = FromRgba(255, 215, 0, 255); + public static readonly Color Gold = FromPixel(new Rgba32(255, 215, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DAA520. /// - public static readonly Color Goldenrod = FromRgba(218, 165, 32, 255); + public static readonly Color Goldenrod = FromPixel(new Rgba32(218, 165, 32, 255)); /// /// Represents a matching the W3C definition that has an hex value of #808080. /// - public static readonly Color Gray = FromRgba(128, 128, 128, 255); + public static readonly Color Gray = FromPixel(new Rgba32(128, 128, 128, 255)); /// /// Represents a matching the W3C definition that has an hex value of #008000. /// - public static readonly Color Green = FromRgba(0, 128, 0, 255); + public static readonly Color Green = FromPixel(new Rgba32(0, 128, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #ADFF2F. /// - public static readonly Color GreenYellow = FromRgba(173, 255, 47, 255); + public static readonly Color GreenYellow = FromPixel(new Rgba32(173, 255, 47, 255)); /// /// Represents a matching the W3C definition that has an hex value of #808080. @@ -299,82 +301,82 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #F0FFF0. /// - public static readonly Color Honeydew = FromRgba(240, 255, 240, 255); + public static readonly Color Honeydew = FromPixel(new Rgba32(240, 255, 240, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF69B4. /// - public static readonly Color HotPink = FromRgba(255, 105, 180, 255); + public static readonly Color HotPink = FromPixel(new Rgba32(255, 105, 180, 255)); /// /// Represents a matching the W3C definition that has an hex value of #CD5C5C. /// - public static readonly Color IndianRed = FromRgba(205, 92, 92, 255); + public static readonly Color IndianRed = FromPixel(new Rgba32(205, 92, 92, 255)); /// /// Represents a matching the W3C definition that has an hex value of #4B0082. /// - public static readonly Color Indigo = FromRgba(75, 0, 130, 255); + public static readonly Color Indigo = FromPixel(new Rgba32(75, 0, 130, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFFF0. /// - public static readonly Color Ivory = FromRgba(255, 255, 240, 255); + public static readonly Color Ivory = FromPixel(new Rgba32(255, 255, 240, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F0E68C. /// - public static readonly Color Khaki = FromRgba(240, 230, 140, 255); + public static readonly Color Khaki = FromPixel(new Rgba32(240, 230, 140, 255)); /// /// Represents a matching the W3C definition that has an hex value of #E6E6FA. /// - public static readonly Color Lavender = FromRgba(230, 230, 250, 255); + public static readonly Color Lavender = FromPixel(new Rgba32(230, 230, 250, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFF0F5. /// - public static readonly Color LavenderBlush = FromRgba(255, 240, 245, 255); + public static readonly Color LavenderBlush = FromPixel(new Rgba32(255, 240, 245, 255)); /// /// Represents a matching the W3C definition that has an hex value of #7CFC00. /// - public static readonly Color LawnGreen = FromRgba(124, 252, 0, 255); + public static readonly Color LawnGreen = FromPixel(new Rgba32(124, 252, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFACD. /// - public static readonly Color LemonChiffon = FromRgba(255, 250, 205, 255); + public static readonly Color LemonChiffon = FromPixel(new Rgba32(255, 250, 205, 255)); /// /// Represents a matching the W3C definition that has an hex value of #ADD8E6. /// - public static readonly Color LightBlue = FromRgba(173, 216, 230, 255); + public static readonly Color LightBlue = FromPixel(new Rgba32(173, 216, 230, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F08080. /// - public static readonly Color LightCoral = FromRgba(240, 128, 128, 255); + public static readonly Color LightCoral = FromPixel(new Rgba32(240, 128, 128, 255)); /// /// Represents a matching the W3C definition that has an hex value of #E0FFFF. /// - public static readonly Color LightCyan = FromRgba(224, 255, 255, 255); + public static readonly Color LightCyan = FromPixel(new Rgba32(224, 255, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FAFAD2. /// - public static readonly Color LightGoldenrodYellow = FromRgba(250, 250, 210, 255); + public static readonly Color LightGoldenrodYellow = FromPixel(new Rgba32(250, 250, 210, 255)); /// /// Represents a matching the W3C definition that has an hex value of #D3D3D3. /// - public static readonly Color LightGray = FromRgba(211, 211, 211, 255); + public static readonly Color LightGray = FromPixel(new Rgba32(211, 211, 211, 255)); /// /// Represents a matching the W3C definition that has an hex value of #90EE90. /// - public static readonly Color LightGreen = FromRgba(144, 238, 144, 255); + public static readonly Color LightGreen = FromPixel(new Rgba32(144, 238, 144, 255)); /// /// Represents a matching the W3C definition that has an hex value of #D3D3D3. @@ -384,27 +386,27 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #FFB6C1. /// - public static readonly Color LightPink = FromRgba(255, 182, 193, 255); + public static readonly Color LightPink = FromPixel(new Rgba32(255, 182, 193, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFA07A. /// - public static readonly Color LightSalmon = FromRgba(255, 160, 122, 255); + public static readonly Color LightSalmon = FromPixel(new Rgba32(255, 160, 122, 255)); /// /// Represents a matching the W3C definition that has an hex value of #20B2AA. /// - public static readonly Color LightSeaGreen = FromRgba(32, 178, 170, 255); + public static readonly Color LightSeaGreen = FromPixel(new Rgba32(32, 178, 170, 255)); /// /// Represents a matching the W3C definition that has an hex value of #87CEFA. /// - public static readonly Color LightSkyBlue = FromRgba(135, 206, 250, 255); + public static readonly Color LightSkyBlue = FromPixel(new Rgba32(135, 206, 250, 255)); /// /// Represents a matching the W3C definition that has an hex value of #778899. /// - public static readonly Color LightSlateGray = FromRgba(119, 136, 153, 255); + public static readonly Color LightSlateGray = FromPixel(new Rgba32(119, 136, 153, 255)); /// /// Represents a matching the W3C definition that has an hex value of #778899. @@ -414,27 +416,27 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #B0C4DE. /// - public static readonly Color LightSteelBlue = FromRgba(176, 196, 222, 255); + public static readonly Color LightSteelBlue = FromPixel(new Rgba32(176, 196, 222, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFFE0. /// - public static readonly Color LightYellow = FromRgba(255, 255, 224, 255); + public static readonly Color LightYellow = FromPixel(new Rgba32(255, 255, 224, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00FF00. /// - public static readonly Color Lime = FromRgba(0, 255, 0, 255); + public static readonly Color Lime = FromPixel(new Rgba32(0, 255, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #32CD32. /// - public static readonly Color LimeGreen = FromRgba(50, 205, 50, 255); + public static readonly Color LimeGreen = FromPixel(new Rgba32(50, 205, 50, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FAF0E6. /// - public static readonly Color Linen = FromRgba(250, 240, 230, 255); + public static readonly Color Linen = FromPixel(new Rgba32(250, 240, 230, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. @@ -444,237 +446,237 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #800000. /// - public static readonly Color Maroon = FromRgba(128, 0, 0, 255); + public static readonly Color Maroon = FromPixel(new Rgba32(128, 0, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #66CDAA. /// - public static readonly Color MediumAquamarine = FromRgba(102, 205, 170, 255); + public static readonly Color MediumAquamarine = FromPixel(new Rgba32(102, 205, 170, 255)); /// /// Represents a matching the W3C definition that has an hex value of #0000CD. /// - public static readonly Color MediumBlue = FromRgba(0, 0, 205, 255); + public static readonly Color MediumBlue = FromPixel(new Rgba32(0, 0, 205, 255)); /// /// Represents a matching the W3C definition that has an hex value of #BA55D3. /// - public static readonly Color MediumOrchid = FromRgba(186, 85, 211, 255); + public static readonly Color MediumOrchid = FromPixel(new Rgba32(186, 85, 211, 255)); /// /// Represents a matching the W3C definition that has an hex value of #9370DB. /// - public static readonly Color MediumPurple = FromRgba(147, 112, 219, 255); + public static readonly Color MediumPurple = FromPixel(new Rgba32(147, 112, 219, 255)); /// /// Represents a matching the W3C definition that has an hex value of #3CB371. /// - public static readonly Color MediumSeaGreen = FromRgba(60, 179, 113, 255); + public static readonly Color MediumSeaGreen = FromPixel(new Rgba32(60, 179, 113, 255)); /// /// Represents a matching the W3C definition that has an hex value of #7B68EE. /// - public static readonly Color MediumSlateBlue = FromRgba(123, 104, 238, 255); + public static readonly Color MediumSlateBlue = FromPixel(new Rgba32(123, 104, 238, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00FA9A. /// - public static readonly Color MediumSpringGreen = FromRgba(0, 250, 154, 255); + public static readonly Color MediumSpringGreen = FromPixel(new Rgba32(0, 250, 154, 255)); /// /// Represents a matching the W3C definition that has an hex value of #48D1CC. /// - public static readonly Color MediumTurquoise = FromRgba(72, 209, 204, 255); + public static readonly Color MediumTurquoise = FromPixel(new Rgba32(72, 209, 204, 255)); /// /// Represents a matching the W3C definition that has an hex value of #C71585. /// - public static readonly Color MediumVioletRed = FromRgba(199, 21, 133, 255); + public static readonly Color MediumVioletRed = FromPixel(new Rgba32(199, 21, 133, 255)); /// /// Represents a matching the W3C definition that has an hex value of #191970. /// - public static readonly Color MidnightBlue = FromRgba(25, 25, 112, 255); + public static readonly Color MidnightBlue = FromPixel(new Rgba32(25, 25, 112, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F5FFFA. /// - public static readonly Color MintCream = FromRgba(245, 255, 250, 255); + public static readonly Color MintCream = FromPixel(new Rgba32(245, 255, 250, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFE4E1. /// - public static readonly Color MistyRose = FromRgba(255, 228, 225, 255); + public static readonly Color MistyRose = FromPixel(new Rgba32(255, 228, 225, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFE4B5. /// - public static readonly Color Moccasin = FromRgba(255, 228, 181, 255); + public static readonly Color Moccasin = FromPixel(new Rgba32(255, 228, 181, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFDEAD. /// - public static readonly Color NavajoWhite = FromRgba(255, 222, 173, 255); + public static readonly Color NavajoWhite = FromPixel(new Rgba32(255, 222, 173, 255)); /// /// Represents a matching the W3C definition that has an hex value of #000080. /// - public static readonly Color Navy = FromRgba(0, 0, 128, 255); + public static readonly Color Navy = FromPixel(new Rgba32(0, 0, 128, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FDF5E6. /// - public static readonly Color OldLace = FromRgba(253, 245, 230, 255); + public static readonly Color OldLace = FromPixel(new Rgba32(253, 245, 230, 255)); /// /// Represents a matching the W3C definition that has an hex value of #808000. /// - public static readonly Color Olive = FromRgba(128, 128, 0, 255); + public static readonly Color Olive = FromPixel(new Rgba32(128, 128, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #6B8E23. /// - public static readonly Color OliveDrab = FromRgba(107, 142, 35, 255); + public static readonly Color OliveDrab = FromPixel(new Rgba32(107, 142, 35, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFA500. /// - public static readonly Color Orange = FromRgba(255, 165, 0, 255); + public static readonly Color Orange = FromPixel(new Rgba32(255, 165, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF4500. /// - public static readonly Color OrangeRed = FromRgba(255, 69, 0, 255); + public static readonly Color OrangeRed = FromPixel(new Rgba32(255, 69, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DA70D6. /// - public static readonly Color Orchid = FromRgba(218, 112, 214, 255); + public static readonly Color Orchid = FromPixel(new Rgba32(218, 112, 214, 255)); /// /// Represents a matching the W3C definition that has an hex value of #EEE8AA. /// - public static readonly Color PaleGoldenrod = FromRgba(238, 232, 170, 255); + public static readonly Color PaleGoldenrod = FromPixel(new Rgba32(238, 232, 170, 255)); /// /// Represents a matching the W3C definition that has an hex value of #98FB98. /// - public static readonly Color PaleGreen = FromRgba(152, 251, 152, 255); + public static readonly Color PaleGreen = FromPixel(new Rgba32(152, 251, 152, 255)); /// /// Represents a matching the W3C definition that has an hex value of #AFEEEE. /// - public static readonly Color PaleTurquoise = FromRgba(175, 238, 238, 255); + public static readonly Color PaleTurquoise = FromPixel(new Rgba32(175, 238, 238, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DB7093. /// - public static readonly Color PaleVioletRed = FromRgba(219, 112, 147, 255); + public static readonly Color PaleVioletRed = FromPixel(new Rgba32(219, 112, 147, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFEFD5. /// - public static readonly Color PapayaWhip = FromRgba(255, 239, 213, 255); + public static readonly Color PapayaWhip = FromPixel(new Rgba32(255, 239, 213, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFDAB9. /// - public static readonly Color PeachPuff = FromRgba(255, 218, 185, 255); + public static readonly Color PeachPuff = FromPixel(new Rgba32(255, 218, 185, 255)); /// /// Represents a matching the W3C definition that has an hex value of #CD853F. /// - public static readonly Color Peru = FromRgba(205, 133, 63, 255); + public static readonly Color Peru = FromPixel(new Rgba32(205, 133, 63, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFC0CB. /// - public static readonly Color Pink = FromRgba(255, 192, 203, 255); + public static readonly Color Pink = FromPixel(new Rgba32(255, 192, 203, 255)); /// /// Represents a matching the W3C definition that has an hex value of #DDA0DD. /// - public static readonly Color Plum = FromRgba(221, 160, 221, 255); + public static readonly Color Plum = FromPixel(new Rgba32(221, 160, 221, 255)); /// /// Represents a matching the W3C definition that has an hex value of #B0E0E6. /// - public static readonly Color PowderBlue = FromRgba(176, 224, 230, 255); + public static readonly Color PowderBlue = FromPixel(new Rgba32(176, 224, 230, 255)); /// /// Represents a matching the W3C definition that has an hex value of #800080. /// - public static readonly Color Purple = FromRgba(128, 0, 128, 255); + public static readonly Color Purple = FromPixel(new Rgba32(128, 0, 128, 255)); /// /// Represents a matching the W3C definition that has an hex value of #663399. /// - public static readonly Color RebeccaPurple = FromRgba(102, 51, 153, 255); + public static readonly Color RebeccaPurple = FromPixel(new Rgba32(102, 51, 153, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF0000. /// - public static readonly Color Red = FromRgba(255, 0, 0, 255); + public static readonly Color Red = FromPixel(new Rgba32(255, 0, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #BC8F8F. /// - public static readonly Color RosyBrown = FromRgba(188, 143, 143, 255); + public static readonly Color RosyBrown = FromPixel(new Rgba32(188, 143, 143, 255)); /// /// Represents a matching the W3C definition that has an hex value of #4169E1. /// - public static readonly Color RoyalBlue = FromRgba(65, 105, 225, 255); + public static readonly Color RoyalBlue = FromPixel(new Rgba32(65, 105, 225, 255)); /// /// Represents a matching the W3C definition that has an hex value of #8B4513. /// - public static readonly Color SaddleBrown = FromRgba(139, 69, 19, 255); + public static readonly Color SaddleBrown = FromPixel(new Rgba32(139, 69, 19, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FA8072. /// - public static readonly Color Salmon = FromRgba(250, 128, 114, 255); + public static readonly Color Salmon = FromPixel(new Rgba32(250, 128, 114, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F4A460. /// - public static readonly Color SandyBrown = FromRgba(244, 164, 96, 255); + public static readonly Color SandyBrown = FromPixel(new Rgba32(244, 164, 96, 255)); /// /// Represents a matching the W3C definition that has an hex value of #2E8B57. /// - public static readonly Color SeaGreen = FromRgba(46, 139, 87, 255); + public static readonly Color SeaGreen = FromPixel(new Rgba32(46, 139, 87, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFF5EE. /// - public static readonly Color SeaShell = FromRgba(255, 245, 238, 255); + public static readonly Color SeaShell = FromPixel(new Rgba32(255, 245, 238, 255)); /// /// Represents a matching the W3C definition that has an hex value of #A0522D. /// - public static readonly Color Sienna = FromRgba(160, 82, 45, 255); + public static readonly Color Sienna = FromPixel(new Rgba32(160, 82, 45, 255)); /// /// Represents a matching the W3C definition that has an hex value of #C0C0C0. /// - public static readonly Color Silver = FromRgba(192, 192, 192, 255); + public static readonly Color Silver = FromPixel(new Rgba32(192, 192, 192, 255)); /// /// Represents a matching the W3C definition that has an hex value of #87CEEB. /// - public static readonly Color SkyBlue = FromRgba(135, 206, 235, 255); + public static readonly Color SkyBlue = FromPixel(new Rgba32(135, 206, 235, 255)); /// /// Represents a matching the W3C definition that has an hex value of #6A5ACD. /// - public static readonly Color SlateBlue = FromRgba(106, 90, 205, 255); + public static readonly Color SlateBlue = FromPixel(new Rgba32(106, 90, 205, 255)); /// /// Represents a matching the W3C definition that has an hex value of #708090. /// - public static readonly Color SlateGray = FromRgba(112, 128, 144, 255); + public static readonly Color SlateGray = FromPixel(new Rgba32(112, 128, 144, 255)); /// /// Represents a matching the W3C definition that has an hex value of #708090. @@ -684,81 +686,80 @@ public readonly partial struct Color /// /// Represents a matching the W3C definition that has an hex value of #FFFAFA. /// - public static readonly Color Snow = FromRgba(255, 250, 250, 255); + public static readonly Color Snow = FromPixel(new Rgba32(255, 250, 250, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00FF7F. /// - public static readonly Color SpringGreen = FromRgba(0, 255, 127, 255); + public static readonly Color SpringGreen = FromPixel(new Rgba32(0, 255, 127, 255)); /// /// Represents a matching the W3C definition that has an hex value of #4682B4. /// - public static readonly Color SteelBlue = FromRgba(70, 130, 180, 255); + public static readonly Color SteelBlue = FromPixel(new Rgba32(70, 130, 180, 255)); /// /// Represents a matching the W3C definition that has an hex value of #D2B48C. /// - public static readonly Color Tan = FromRgba(210, 180, 140, 255); + public static readonly Color Tan = FromPixel(new Rgba32(210, 180, 140, 255)); /// /// Represents a matching the W3C definition that has an hex value of #008080. /// - public static readonly Color Teal = FromRgba(0, 128, 128, 255); + public static readonly Color Teal = FromPixel(new Rgba32(0, 128, 128, 255)); /// /// Represents a matching the W3C definition that has an hex value of #D8BFD8. /// - public static readonly Color Thistle = FromRgba(216, 191, 216, 255); + public static readonly Color Thistle = FromPixel(new Rgba32(216, 191, 216, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FF6347. /// - public static readonly Color Tomato = FromRgba(255, 99, 71, 255); + public static readonly Color Tomato = FromPixel(new Rgba32(255, 99, 71, 255)); /// /// Represents a matching the W3C definition that has an hex value of #00000000. /// - public static readonly Color Transparent = FromRgba(0, 0, 0, 0); + public static readonly Color Transparent = FromPixel(new Rgba32(0, 0, 0, 0)); /// /// Represents a matching the W3C definition that has an hex value of #40E0D0. /// - public static readonly Color Turquoise = FromRgba(64, 224, 208, 255); + public static readonly Color Turquoise = FromPixel(new Rgba32(64, 224, 208, 255)); /// /// Represents a matching the W3C definition that has an hex value of #EE82EE. /// - public static readonly Color Violet = FromRgba(238, 130, 238, 255); + public static readonly Color Violet = FromPixel(new Rgba32(238, 130, 238, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F5DEB3. /// - public static readonly Color Wheat = FromRgba(245, 222, 179, 255); + public static readonly Color Wheat = FromPixel(new Rgba32(245, 222, 179, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly Color White = FromRgba(255, 255, 255, 255); + public static readonly Color White = FromPixel(new Rgba32(255, 255, 255, 255)); /// /// Represents a matching the W3C definition that has an hex value of #F5F5F5. /// - public static readonly Color WhiteSmoke = FromRgba(245, 245, 245, 255); + public static readonly Color WhiteSmoke = FromPixel(new Rgba32(245, 245, 245, 255)); /// /// Represents a matching the W3C definition that has an hex value of #FFFF00. /// - public static readonly Color Yellow = FromRgba(255, 255, 0, 255); + public static readonly Color Yellow = FromPixel(new Rgba32(255, 255, 0, 255)); /// /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// - public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); + public static readonly Color YellowGreen = FromPixel(new Rgba32(154, 205, 50, 255)); private static Dictionary CreateNamedColorsLookup() - { - return new Dictionary(StringComparer.OrdinalIgnoreCase) + => new(StringComparer.OrdinalIgnoreCase) { { nameof(AliceBlue), AliceBlue }, { nameof(AntiqueWhite), AntiqueWhite }, @@ -910,5 +911,4 @@ public readonly partial struct Color { nameof(Yellow), Yellow }, { nameof(YellowGreen), YellowGreen } }; - } } diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 711009050..ec19a86eb 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp; @@ -19,33 +18,24 @@ namespace SixLabors.ImageSharp; /// public readonly partial struct Color : IEquatable { - private readonly Rgba64 data; + private readonly Vector4 data; private readonly IPixel? boxedHighPrecisionPixel; + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. [MethodImpl(InliningOptions.ShortMethod)] - private Color(byte r, byte g, byte b, byte a) - { - this.data = new Rgba64( - ColorNumerics.UpscaleFrom8BitTo16Bit(r), - ColorNumerics.UpscaleFrom8BitTo16Bit(g), - ColorNumerics.UpscaleFrom8BitTo16Bit(b), - ColorNumerics.UpscaleFrom8BitTo16Bit(a)); - - this.boxedHighPrecisionPixel = null; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private Color(byte r, byte g, byte b) + private Color(Vector4 vector) { - this.data = new Rgba64( - ColorNumerics.UpscaleFrom8BitTo16Bit(r), - ColorNumerics.UpscaleFrom8BitTo16Bit(g), - ColorNumerics.UpscaleFrom8BitTo16Bit(b), - ushort.MaxValue); - + this.data = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); this.boxedHighPrecisionPixel = null; } + /// + /// Initializes a new instance of the struct. + /// + /// The pixel containing color information. [MethodImpl(InliningOptions.ShortMethod)] private Color(IPixel pixel) { @@ -53,6 +43,21 @@ public readonly partial struct Color : IEquatable this.data = default; } + /// + /// Converts a to . + /// + /// The . + /// The . + public static explicit operator Vector4(Color color) => color.ToScaledVector4(); + + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static explicit operator Color(Vector4 source) => new(source); + /// /// Checks whether two structures are equal. /// @@ -77,27 +82,6 @@ public readonly partial struct Color : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(Color left, Color right) => !left.Equals(right); - /// - /// Creates a from RGBA bytes. - /// - /// The red component (0-255). - /// The green component (0-255). - /// The blue component (0-255). - /// The alpha component (0-255). - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Color FromRgba(byte r, byte g, byte b, byte a) => new(r, g, b, a); - - /// - /// Creates a from RGB bytes. - /// - /// The red component (0-255). - /// The green component (0-255). - /// The blue component (0-255). - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Color FromRgb(byte r, byte g, byte b) => new(r, g, b); - /// /// Creates a from the given . /// @@ -108,34 +92,45 @@ public readonly partial struct Color : IEquatable public static Color FromPixel(TPixel pixel) where TPixel : unmanaged, IPixel { - // Avoid boxing in case we can convert to Rgba64 safely and efficiently - if (typeof(TPixel) == typeof(Rgba64)) - { - return new((Rgba64)(object)pixel); - } - else if (typeof(TPixel) == typeof(Rgb48)) - { - return new((Rgb48)(object)pixel); - } - else if (typeof(TPixel) == typeof(La32)) + // Avoid boxing in case we can convert to Vector4 safely and efficiently + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) { - return new((La32)(object)pixel); + return new(pixel.ToScaledVector4()); } - else if (typeof(TPixel) == typeof(L16)) + else { - return new((L16)(object)pixel); + return new(pixel); } + } + /// + /// Bulk converts a span of a specified type to a span of . + /// + /// The pixel type to convert to. + /// The source pixel span. + /// The destination color span. + [MethodImpl(InliningOptions.ShortMethod)] + public static void FromPixel(ReadOnlySpan source, Span destination) + where TPixel : unmanaged, IPixel + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.MaxComponentPrecision <= PixelComponentPrecision.Byte) + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) { - Rgba32 p = default; - pixel.ToRgba32(ref p); - return new(p); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = new(source[i].ToScaledVector4()); + } } else { - return new(pixel); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = new(source[i]); + } } } @@ -154,8 +149,7 @@ public readonly partial struct Color : IEquatable public static Color ParseHex(string hex) { Rgba32 rgba = Rgba32.ParseHex(hex); - - return new Color(rgba); + return FromPixel(rgba); } /// @@ -177,7 +171,7 @@ public readonly partial struct Color : IEquatable if (Rgba32.TryParseHex(hex, out Rgba32 rgba)) { - result = new Color(rgba); + result = FromPixel(rgba); return true; } @@ -256,14 +250,15 @@ public readonly partial struct Color : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public string ToHex() { + Rgba32 rgba = default; if (this.boxedHighPrecisionPixel is not null) { - Rgba32 rgba = default; this.boxedHighPrecisionPixel.ToRgba32(ref rgba); return rgba.ToHex(); } - return this.data.ToRgba32().ToHex(); + rgba.FromScaledVector4(this.data); + return rgba.ToHex(); } /// @@ -286,7 +281,7 @@ public readonly partial struct Color : IEquatable if (this.boxedHighPrecisionPixel is null) { pixel = default; - pixel.FromRgba64(this.data); + pixel.FromScaledVector4(this.data); return pixel; } @@ -305,7 +300,8 @@ public readonly partial struct Color : IEquatable public static void ToPixel(ReadOnlySpan source, Span destination) where TPixel : unmanaged, IPixel { - // TODO: Investigate bulk operations utilizing configuration parameter here. + // We cannot use bulk pixel operations here as there is no guarantee that the source colors are + // created from pixel formats which fit into the unboxed vector data. Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); for (int i = 0; i < source.Length; i++) { @@ -319,7 +315,7 @@ public readonly partial struct Color : IEquatable { if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null) { - return this.data.PackedValue == other.data.PackedValue; + return this.data == other.data; } return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true; @@ -334,9 +330,20 @@ public readonly partial struct Color : IEquatable { if (this.boxedHighPrecisionPixel is null) { - return this.data.PackedValue.GetHashCode(); + return this.data.GetHashCode(); } return this.boxedHighPrecisionPixel.GetHashCode(); } + + [MethodImpl(InliningOptions.ShortMethod)] + private Vector4 ToScaledVector4() + { + if (this.boxedHighPrecisionPixel is null) + { + return this.data; + } + + return this.boxedHighPrecisionPixel.ToScaledVector4(); + } } diff --git a/src/ImageSharp/Formats/AnimationUtilities.cs b/src/ImageSharp/Formats/AnimationUtilities.cs index 288f3d132..4605d4daa 100644 --- a/src/ImageSharp/Formats/AnimationUtilities.cs +++ b/src/ImageSharp/Formats/AnimationUtilities.cs @@ -50,7 +50,7 @@ internal static class AnimationUtilities Span next = buffers.GetSpan().Slice(currentFrame.Width * 2, currentFrame.Width); Span result = buffers.GetSpan()[(currentFrame.Width * 3)..]; - Rgba32 bg = replacement; + Rgba32 bg = replacement.ToPixel(); int top = int.MinValue; int bottom = int.MaxValue; @@ -232,7 +232,7 @@ internal static class AnimationUtilities ref Rgba32 r = ref Unsafe.Add(ref MemoryMarshal.GetReference(result), x); bool peq = c.Rgba == (previousFrame != null ? p.Rgba : bg.Rgba); - Rgba32 val = (blend & peq) ? replacement : c; + Rgba32 val = (blend & peq) ? bg : c; peq &= nextFrame == null || (n.Rgba >> 24 >= c.Rgba >> 24); r = val; diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index aecbbbbc7..8916da6e0 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -711,10 +711,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals Color[] colorTable = new Color[this.imageDescriptor.LocalColorTableSize]; ReadOnlySpan rgbTable = MemoryMarshal.Cast(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]); - for (int i = 0; i < colorTable.Length; i++) - { - colorTable[i] = new Color(rgbTable[i]); - } + Color.FromPixel(rgbTable, colorTable); gifMeta.LocalColorTable = colorTable; } @@ -789,10 +786,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals Color[] colorTable = new Color[this.logicalScreenDescriptor.GlobalColorTableSize]; ReadOnlySpan rgbTable = MemoryMarshal.Cast(globalColorTableSpan); - for (int i = 0; i < colorTable.Length; i++) - { - colorTable[i] = new Color(rgbTable[i]); - } + Color.FromPixel(rgbTable, colorTable); this.gifMetadata.GlobalColorTable = colorTable; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 3eabbdef0..4178d2820 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1240,10 +1240,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals Color[] colorTable = new Color[palette.Length / Unsafe.SizeOf()]; ReadOnlySpan rgbTable = MemoryMarshal.Cast(palette); - for (int i = 0; i < colorTable.Length; i++) - { - colorTable[i] = new Color(rgbTable[i]); - } + Color.FromPixel(rgbTable, colorTable); if (alpha.Length > 0) { @@ -1276,14 +1273,14 @@ internal sealed class PngDecoderCore : IImageDecoderInternals ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); - pngMetadata.TransparentColor = new(new Rgb48(rc, gc, bc)); + pngMetadata.TransparentColor = Color.FromPixel(new Rgb48(rc, gc, bc)); return; } byte r = ReadByteLittleEndian(alpha, 0); byte g = ReadByteLittleEndian(alpha, 2); byte b = ReadByteLittleEndian(alpha, 4); - pngMetadata.TransparentColor = new(new Rgb24(r, g, b)); + pngMetadata.TransparentColor = Color.FromPixel(new Rgb24(r, g, b)); } } else if (this.pngColorType == PngColorType.Grayscale) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e348d7467..8bf8be2ad 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -329,7 +329,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { // TODO: We should be able to speed this up with SIMD and masking. Rgba32 rgba32 = default; - Rgba32 transparent = Color.Transparent; + Rgba32 transparent = Color.Transparent.ToPixel(); for (int y = 0; y < accessor.Height; y++) { Span span = accessor.GetRowSpan(y); @@ -1066,7 +1066,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable else { alpha.Clear(); - Rgb24 rgb = pngMetadata.TransparentColor.Value.ToRgb24(); + Rgb24 rgb = pngMetadata.TransparentColor.Value.ToPixel(); alpha[1] = rgb.R; alpha[3] = rgb.G; alpha[5] = rgb.B; diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index f217515e3..39b3fff27 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -202,7 +202,7 @@ internal static class PngScanlineProcessor for (nuint x = pixelOffset, o = 0; x < frameControl.XMax; x += increment, o++) { uint index = Unsafe.Add(ref scanlineSpanRef, o); - pixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToRgba32()); + pixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToPixel()); Unsafe.Add(ref rowSpanRef, x) = pixel; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index a8a70f727..89d1b9d20 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -22,8 +22,8 @@ internal class BlackIsZero1TiffColor : TiffBaseColorDecoder TPixel colorBlack = default; TPixel colorWhite = default; - colorBlack.FromRgba32(Color.Black); - colorWhite.FromRgba32(Color.White); + colorBlack.FromRgba32(Color.Black.ToPixel()); + colorWhite.FromRgba32(Color.White.ToPixel()); ref byte dataRef = ref MemoryMarshal.GetReference(data); for (nuint y = (uint)top; y < (uint)(top + height); y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index c5b662979..4cba8f2d7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -21,8 +21,8 @@ internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder var colorBlack = default(TPixel); var colorWhite = default(TPixel); - colorBlack.FromRgba32(Color.Black); - colorWhite.FromRgba32(Color.White); + colorBlack.FromRgba32(Color.Black.ToPixel()); + colorWhite.FromRgba32(Color.White.ToPixel()); ref byte dataRef = ref MemoryMarshal.GetReference(data); for (nuint y = (uint)top; y < (uint)(top + height); y++) { diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs index 9ffda0f51..c98be1fcd 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp.BitWriter; @@ -157,7 +158,7 @@ internal abstract class BitWriterBase /// The number of times to loop the animation. If it is 0, this means infinitely. public static void WriteAnimationParameter(Stream stream, Color background, ushort loopCount) { - WebpAnimationParameter chunk = new(background.ToBgra32().PackedValue, loopCount); + WebpAnimationParameter chunk = new(background.ToPixel().PackedValue, loopCount); chunk.WriteTo(stream); } diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 65f1a4da4..70372fe98 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -102,7 +102,7 @@ internal class WebpAnimationDecoder : IDisposable { case WebpChunkType.FrameData: Color backgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore - ? new Color(new Bgra32(0, 0, 0, 0)) + ? Color.FromPixel(new Bgra32(0, 0, 0, 0)) : features.AnimationBackgroundColor!.Value; uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, backgroundColor); remainingBytes -= (int)dataSize; diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 69a0afcd9..2991f355f 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -438,7 +438,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable byte green = (byte)stream.ReadByte(); byte red = (byte)stream.ReadByte(); byte alpha = (byte)stream.ReadByte(); - features.AnimationBackgroundColor = new Color(new Rgba32(red, green, blue, alpha)); + features.AnimationBackgroundColor = Color.FromPixel(new Rgba32(red, green, blue, alpha)); int bytesRead = stream.Read(buffer, 0, 2); if (bytesRead != 2) { diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index 00319e9b5..c0d1f27ca 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp; diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 38f0b94d6..e12631cbd 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -4,7 +4,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index adbf68861..b28911a90 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs new file mode 100644 index 000000000..a76105414 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Represents pixel component information within a pixel format. +/// +public readonly struct PixelComponentInfo +{ + private readonly long precisionData1; + private readonly long precisionData2; + + private PixelComponentInfo(int count, int padding, long precisionData1, long precisionData2) + { + this.ComponentCount = count; + this.Padding = padding; + this.precisionData1 = precisionData1; + this.precisionData2 = precisionData2; + } + + /// + /// Gets the number of components within the pixel. + /// + public int ComponentCount { get; } + + /// + /// Gets the number of bytes of padding within the pixel. + /// + public int Padding { get; } + + /// + /// Creates a new instance. + /// + /// The type of pixel format. + /// The number of components within the pixel format. + /// The precision in bits of each component. + /// The . + /// The component precision and index cannot exceed the component range. + public static PixelComponentInfo Create(int count, params int[] precision) + where TPixel : unmanaged, IPixel + { + if (precision.Length != count || precision.Length > 16) + { + throw new ArgumentException($"Count must match the length of precision array and cannot exceed 16."); + } + + long precisionData1 = 0; + long precisionData2 = 0; + int sum = 0; + for (int i = 0; i < precision.Length; i++) + { + int p = precision[i]; + if (p is < 0 or > 255) + { + throw new ArgumentException("Precision must be between 0 and 255."); + } + + if (i < 8) + { + precisionData1 |= ((long)p) << (8 * i); + } + else + { + precisionData2 |= ((long)p) << (8 * (i - 8)); + } + + sum += p; + } + + return new PixelComponentInfo(count, (Unsafe.SizeOf() * 8) - sum, precisionData1, precisionData2); + } + + /// + /// Returns the precision of the component in bits at the given index. + /// + /// The component index. + /// The . + /// The component index cannot exceed the component range. + public int GetComponentPrecision(int componentIndex) + { + if (componentIndex < 0 || componentIndex >= this.ComponentCount) + { + throw new ArgumentOutOfRangeException($"Component index must be between 0 and {this.ComponentCount - 1} inclusive."); + } + + long selectedPrecisionData = componentIndex < 8 ? this.precisionData1 : this.precisionData2; + return (int)((selectedPrecisionData >> (8 * (componentIndex & 7))) & 0xFF); + } + + /// + /// Returns the maximum precision in bits of all components. + /// + /// The . + public int GetMaximumComponentPrecision() + { + int maxPrecision = 0; + for (int i = 0; i < this.ComponentCount; i++) + { + int componentPrecision = this.GetComponentPrecision(i); + if (componentPrecision > maxPrecision) + { + maxPrecision = componentPrecision; + } + } + + return maxPrecision; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs index 3480ac76e..2f15b7fad 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs @@ -4,47 +4,67 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Provides enumeration of the precision of individual components within a pixel format. +/// Provides enumeration of the precision in bits of individual components within a pixel format. /// public enum PixelComponentPrecision { /// /// 8-bit signed integer. /// - SByte, + SByte = sizeof(sbyte) * 8, /// /// 8-bit unsigned integer. /// - Byte, + Byte = sizeof(byte) * 8, /// /// 16-bit signed integer. /// - Short, + Short = sizeof(short) * 8, /// /// 16-bit unsigned integer. /// - UShort, + UShort = sizeof(ushort) * 8, /// /// 32-bit signed integer. /// - Int, + Int = sizeof(int) * 8, /// /// 32-bit unsigned integer. /// - UInt, + UInt = sizeof(uint) * 8, + + /// + /// 64-bit signed integer. + /// + Long = sizeof(long) * 8, + + /// + /// 64-bit unsigned integer. + /// + ULong = sizeof(ulong) * 8, /// /// 16-bit floating point. /// - Half, + Half = (sizeof(float) * 8) / 2, /// /// 32-bit floating point. /// - Float + Float = sizeof(float) * 8, + + /// + /// 64-bit floating point. + /// + Double = sizeof(double) * 8, + + /// + /// 128-bit floating point. + /// + Decimal = sizeof(decimal) * 8, } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 33fa62d1a..abae4f246 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -57,7 +56,7 @@ public partial struct A8 : IPixel, IPackedVector public static bool operator !=(A8 left, A8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 66523f040..e1399f05c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -146,22 +145,6 @@ public partial struct Abgr32 : IPixel, IPackedVector set => this.Abgr = value; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Abgr32 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Abgr32(Color color) => color.ToAbgr32(); - /// /// Compares two objects for equality. /// @@ -185,7 +168,7 @@ public partial struct Abgr32 : IPixel, IPackedVector public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index d3375f65f..2b7bcf913 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -146,22 +145,6 @@ public partial struct Argb32 : IPixel, IPackedVector set => this.Argb = value; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Argb32 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Argb32(Color color) => color.ToArgb32(); - /// /// Compares two objects for equality. /// @@ -185,7 +168,7 @@ public partial struct Argb32 : IPixel, IPackedVector public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 9d0186c83..c9c324680 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -50,22 +49,6 @@ public partial struct Bgr24 : IPixel this.B = b; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Bgr24 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Bgr24(Color color) => color.ToBgr24(); - /// /// Compares two objects for equality. /// @@ -89,7 +72,7 @@ public partial struct Bgr24 : IPixel public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 8, 8, 8), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 6135ab3e7..d020cf025 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -61,7 +60,7 @@ public partial struct Bgr565 : IPixel, IPackedVector public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 5, 6, 5), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 7da4fad4f..b3a96523e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -99,22 +98,6 @@ public partial struct Bgra32 : IPixel, IPackedVector set => this.Bgra = value; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Bgra32 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Bgra32(Color color) => color.ToBgra32(); - /// /// Compares two objects for equality. /// @@ -138,7 +121,7 @@ public partial struct Bgra32 : IPixel, IPackedVector public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index effc0d6e5..510d6666f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -59,7 +58,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 4, 4, 4, 4), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 14110b1d1..68dbd9544 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -62,7 +61,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 5, 5, 5, 1), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index f9a36305d..fbe7a0c52 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -62,7 +61,7 @@ public partial struct Byte4 : IPixel, IPackedVector public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 7e1997251..aca64ae7a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -47,7 +46,7 @@ public partial struct HalfSingle : IPixel, IPackedVector public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Half, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 16), PixelAlphaRepresentation.None); /// public PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 1e3854feb..5a25d523b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -54,7 +53,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Half, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 233160579..7ad7b07f4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -59,7 +58,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Half, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index fa07d650b..0c58fb1fd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -49,7 +48,7 @@ public partial struct L16 : IPixel, IPackedVector public static bool operator !=(L16 left, L16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 28fb7ec72..2cd8b20e7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -50,7 +49,7 @@ public partial struct L8 : IPixel, IPackedVector public static bool operator !=(L8 left, L8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(1, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 8), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 99203518a..b811df4b4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -73,7 +72,7 @@ public partial struct La16 : IPixel, IPackedVector public static bool operator !=(La16 left, La16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index a8c0dcd94..e6a63f5ec 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -75,7 +74,7 @@ public partial struct La32 : IPixel, IPackedVector public static bool operator !=(La32 left, La32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index 5db53e853..f7427e65c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -62,7 +61,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.SByte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 4c850e426..beeae655a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -64,7 +63,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.SByte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 7fd299d6d..8152c89d3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -63,7 +62,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Short, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index ff5cd6a98..3f37cb80f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -65,7 +64,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Short, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 9a2753341..594bdd9a1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -59,7 +58,7 @@ public partial struct Rg32 : IPixel, IPackedVector public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index f4c907abf..8d0c7cd48 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -53,22 +52,6 @@ public partial struct Rgb24 : IPixel this.B = b; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Rgb24 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb24(Color color) => color.ToRgb24(); - /// /// Allows the implicit conversion of an instance of to a /// . @@ -108,7 +91,7 @@ public partial struct Rgb24 : IPixel public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 8, 8, 8), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 642883ff0..f7aac32f4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -71,7 +70,7 @@ public partial struct Rgb48 : IPixel public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(3, PixelComponentPrecision.UShort, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 16, 16, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 5f289577f..84482a7aa 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -62,7 +61,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 10, 10, 10, 2), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index f775e8ae1..f391e6474 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -6,7 +6,6 @@ using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -175,22 +174,6 @@ public partial struct Rgba32 : IPixel, IPackedVector set => this.Rgba = value; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Rgba32 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgba32(Color color) => color.ToRgba32(); - /// /// Allows the implicit conversion of an instance of to a /// . @@ -288,7 +271,7 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Byte, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 84860d66d..47062779b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -169,22 +168,6 @@ public partial struct Rgba64 : IPixel, IPackedVector set => Unsafe.As(ref this) = value; } - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Color(Rgba64 source) => new(source); - - /// - /// Converts a to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgba64(Color color) => color.ToPixel(); - /// /// Compares two objects for equality. /// @@ -208,7 +191,7 @@ public partial struct Rgba64 : IPixel, IPackedVector public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.UShort, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 064064f73..ce1c6d572 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -98,7 +97,7 @@ public partial struct RgbaVector : IPixel public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Float, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 32, 32, 32, 32), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 8af952b18..786c98ee4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -66,7 +65,7 @@ public partial struct Short2 : IPixel, IPackedVector public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Short, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 4c60887df..890e66299 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.PixelFormats; @@ -68,7 +67,7 @@ public partial struct Short4 : IPixel, IPackedVector public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(4, PixelComponentPrecision.Short, PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index cf3707f9e..4d3f9e22d 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs similarity index 65% rename from src/ImageSharp/Formats/PixelTypeInfo.cs rename to src/ImageSharp/PixelFormats/PixelTypeInfo.cs index 170ffcfae..e209b21e1 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -2,12 +2,11 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; // TODO: Review this type as it's used to represent 2 different things. // 1.The encoded image pixel format. // 2. The pixel format of the decoded image. -namespace SixLabors.ImageSharp.Formats; +namespace SixLabors.ImageSharp.PixelFormats; /// /// Contains information about the pixels that make up an images visual data. @@ -23,15 +22,10 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// public int BitsPerPixel { get; init; } = bitsPerPixel; - /// - /// Gets the count of the color components - /// - public int ComponentCount { get; init; } - /// /// Gets the maximum precision of components within the pixel. /// - public PixelComponentPrecision? MaxComponentPrecision { get; init; } + public PixelComponentInfo? ComponentInfo { get; init; } /// /// Gets the pixel alpha transparency behavior. @@ -39,16 +33,21 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// public PixelAlphaRepresentation? AlphaRepresentation { get; init; } - internal static PixelTypeInfo Create( - byte componentCount, - PixelComponentPrecision componentPrecision, - PixelAlphaRepresentation pixelAlphaRepresentation) + /// + /// Creates a new instance. + /// + /// The type of pixel format. + /// The pixel component info. + /// The pixel alpha representation. + /// The . + public static PixelTypeInfo Create( + PixelComponentInfo info, + PixelAlphaRepresentation alphaRepresentation) where TPixel : unmanaged, IPixel => new() { BitsPerPixel = Unsafe.SizeOf() * 8, - ComponentCount = componentCount, - MaxComponentPrecision = componentPrecision, - AlphaRepresentation = pixelAlphaRepresentation + ComponentInfo = info, + AlphaRepresentation = alphaRepresentation }; } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 4a02642fd..c8a381d59 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters; internal class LomographProcessor : FilterProcessor where TPixel : unmanaged, IPixel { - private static readonly Color VeryDarkGreen = Color.FromRgba(0, 10, 0, 255); + private static readonly Color VeryDarkGreen = Color.FromPixel(new Rgba32(0, 10, 0, 255)); private readonly LomographProcessor definition; /// diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index c1b79d10a..84c0364fa 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -12,8 +12,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters; internal class PolaroidProcessor : FilterProcessor where TPixel : unmanaged, IPixel { - private static readonly Color LightOrange = Color.FromRgba(255, 153, 102, 128); - private static readonly Color VeryDarkOrange = Color.FromRgb(102, 34, 0); + private static readonly Color LightOrange = Color.FromPixel(new Rgba32(255, 153, 102, 128)); + private static readonly Color VeryDarkOrange = Color.FromPixel(new Rgb24(102, 34, 0)); private readonly PolaroidProcessor definition; /// diff --git a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs index a6aac20c3..5ba7809e1 100644 --- a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs @@ -21,7 +21,7 @@ public class GetSetPixel public Rgba32 GetSetImageSharp() { using Image image = new(400, 400); - image[200, 200] = Color.White; + image[200, 200] = Color.White.ToPixel(); return image[200, 200]; } } diff --git a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs index 58c23d2fe..82fce7dad 100644 --- a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs @@ -13,7 +13,7 @@ public class BokehBlur [Benchmark] public void Blur() { - using Image image = new(Configuration.Default, 400, 400, Color.White); + using Image image = new(Configuration.Default, 400, 400, Color.White.ToPixel()); image.Mutate(c => c.BokehBlur()); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs index bf760aa2d..fae80fe84 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs @@ -13,7 +13,7 @@ public class Diffuse [Benchmark] public Size DoDiffuse() { - using Image image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond); + using Image image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond.ToPixel()); image.Mutate(x => x.Dither(KnownDitherings.FloydSteinberg)); return image.Size; @@ -22,7 +22,7 @@ public class Diffuse [Benchmark] public Size DoDither() { - using Image image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond); + using Image image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond.ToPixel()); image.Mutate(x => x.Dither()); return image.Size; diff --git a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs index 160df21e6..462d83488 100644 --- a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs @@ -13,7 +13,7 @@ public class GaussianBlur [Benchmark] public void Blur() { - using var image = new Image(Configuration.Default, 400, 400, Color.White); + using Image image = new(Configuration.Default, 400, 400, Color.White.ToPixel()); image.Mutate(c => c.GaussianBlur()); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs index 89d2966d7..b041a9d57 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs @@ -13,7 +13,7 @@ public class Rotate [Benchmark] public Size DoRotate() { - using Image image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond); + using Image image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond.ToPixel()); image.Mutate(x => x.Rotate(37.5F)); return image.Size; diff --git a/tests/ImageSharp.Benchmarks/Processing/Skew.cs b/tests/ImageSharp.Benchmarks/Processing/Skew.cs index baeff29a8..9f0103fa6 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Skew.cs @@ -13,7 +13,7 @@ public class Skew [Benchmark] public Size DoSkew() { - using Image image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond); + using Image image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond.ToPixel()); image.Mutate(x => x.Skew(20, 10)); return image.Size; diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs index 4091e0cd4..2eb821b61 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs @@ -12,10 +12,10 @@ public partial class ColorTests [Fact] public void Rgba64() { - var source = new Rgba64(100, 2222, 3333, 4444); + Rgba64 source = new(100, 2222, 3333, 4444); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Rgba64 data = color.ToPixel(); @@ -25,10 +25,10 @@ public partial class ColorTests [Fact] public void Rgba32() { - var source = new Rgba32(1, 22, 33, 231); + Rgba32 source = new(1, 22, 33, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Rgba32 data = color.ToPixel(); @@ -38,10 +38,10 @@ public partial class ColorTests [Fact] public void Argb32() { - var source = new Argb32(1, 22, 33, 231); + Argb32 source = new(1, 22, 33, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Argb32 data = color.ToPixel(); @@ -51,10 +51,10 @@ public partial class ColorTests [Fact] public void Bgra32() { - var source = new Bgra32(1, 22, 33, 231); + Bgra32 source = new(1, 22, 33, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Bgra32 data = color.ToPixel(); @@ -64,10 +64,10 @@ public partial class ColorTests [Fact] public void Abgr32() { - var source = new Abgr32(1, 22, 33, 231); + Abgr32 source = new(1, 22, 33, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Abgr32 data = color.ToPixel(); @@ -77,10 +77,10 @@ public partial class ColorTests [Fact] public void Rgb24() { - var source = new Rgb24(1, 22, 231); + Rgb24 source = new(1, 22, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Rgb24 data = color.ToPixel(); @@ -90,10 +90,10 @@ public partial class ColorTests [Fact] public void Bgr24() { - var source = new Bgr24(1, 22, 231); + Bgr24 source = new(1, 22, 231); // Act: - Color color = source; + Color color = Color.FromPixel(source); // Assert: Bgr24 data = color.ToPixel(); diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs index dacec7144..14a5a44f1 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -16,10 +16,10 @@ public partial class ColorTests var source = new Rgba64(100, 2222, 3333, 4444); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Rgba64 data = color; + Rgba64 data = color.ToPixel(); Assert.Equal(source, data); } @@ -29,10 +29,10 @@ public partial class ColorTests var source = new Rgba32(1, 22, 33, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Rgba32 data = color; + Rgba32 data = color.ToPixel(); Assert.Equal(source, data); } @@ -42,10 +42,10 @@ public partial class ColorTests var source = new Argb32(1, 22, 33, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Argb32 data = color; + Argb32 data = color.ToPixel(); Assert.Equal(source, data); } @@ -55,10 +55,10 @@ public partial class ColorTests var source = new Bgra32(1, 22, 33, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Bgra32 data = color; + Bgra32 data = color.ToPixel(); Assert.Equal(source, data); } @@ -68,10 +68,10 @@ public partial class ColorTests var source = new Abgr32(1, 22, 33, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Abgr32 data = color; + Abgr32 data = color.ToPixel(); Assert.Equal(source, data); } @@ -81,10 +81,10 @@ public partial class ColorTests var source = new Rgb24(1, 22, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Rgb24 data = color; + Rgb24 data = color.ToPixel(); Assert.Equal(source, data); } @@ -94,10 +94,10 @@ public partial class ColorTests var source = new Bgr24(1, 22, 231); // Act: - var color = new Color(source); + var color = Color.FromPixel(source); // Assert: - Bgr24 data = color; + Bgr24 data = color.ToPixel(); Assert.Equal(source, data); } @@ -105,7 +105,7 @@ public partial class ColorTests public void Vector4Constructor() { // Act: - Color color = new(Vector4.One); + Color color = (Color)Vector4.One; // Assert: Assert.Equal(new RgbaVector(1, 1, 1, 1), color.ToPixel()); @@ -117,7 +117,7 @@ public partial class ColorTests [Fact] public void GenericPixelRoundTrip() { - AssertGenericPixelRoundTrip(new RgbaVector(float.Epsilon, 2 * float.Epsilon, float.MaxValue, float.MinValue)); + AssertGenericPixelRoundTrip(new RgbaVector(0.5f, 0.75f, 1, 0)); AssertGenericPixelRoundTrip(new Rgba64(1, 2, ushort.MaxValue, ushort.MaxValue - 1)); AssertGenericPixelRoundTrip(new Rgb48(1, 2, ushort.MaxValue - 1)); AssertGenericPixelRoundTrip(new La32(1, ushort.MaxValue - 1)); diff --git a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs index 3ea8623d9..9126543e5 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs @@ -12,10 +12,10 @@ public partial class ColorTests [Fact] public void Rgba64() { - var source = new Rgba64(100, 2222, 3333, 4444); + Rgba64 source = new(100, 2222, 3333, 4444); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Rgba64 data = color.ToPixel(); @@ -25,10 +25,10 @@ public partial class ColorTests [Fact] public void Rgba32() { - var source = new Rgba32(1, 22, 33, 231); + Rgba32 source = new(1, 22, 33, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Rgba32 data = color.ToPixel(); @@ -38,10 +38,10 @@ public partial class ColorTests [Fact] public void Argb32() { - var source = new Argb32(1, 22, 33, 231); + Argb32 source = new(1, 22, 33, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Argb32 data = color.ToPixel(); @@ -51,10 +51,10 @@ public partial class ColorTests [Fact] public void Bgra32() { - var source = new Bgra32(1, 22, 33, 231); + Bgra32 source = new(1, 22, 33, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Bgra32 data = color.ToPixel(); @@ -64,10 +64,10 @@ public partial class ColorTests [Fact] public void Abgr32() { - var source = new Abgr32(1, 22, 33, 231); + Abgr32 source = new(1, 22, 33, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Abgr32 data = color.ToPixel(); @@ -77,10 +77,10 @@ public partial class ColorTests [Fact] public void Rgb24() { - var source = new Rgb24(1, 22, 231); + Rgb24 source = new(1, 22, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Rgb24 data = color.ToPixel(); @@ -90,10 +90,10 @@ public partial class ColorTests [Fact] public void Bgr24() { - var source = new Bgr24(1, 22, 231); + Bgr24 source = new(1, 22, 231); // Act: - var color = new Color(source); + Color color = Color.FromPixel(source); // Assert: Bgr24 data = color.ToPixel(); diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs index f7e209217..d430df5b4 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -10,12 +10,12 @@ public partial class ColorTests [Fact] public void WithAlpha() { - var c1 = Color.FromRgba(111, 222, 55, 255); + Color c1 = Color.FromPixel(new Rgba32(111, 222, 55, 255)); Color c2 = c1.WithAlpha(0.5f); - var expected = new Rgba32(111, 222, 55, 128); + Rgba32 expected = new(111, 222, 55, 128); - Assert.Equal(expected, (Rgba32)c2); + Assert.Equal(expected, c2.ToPixel()); } [Theory] @@ -23,13 +23,13 @@ public partial class ColorTests [InlineData(true)] public void Equality_WhenTrue(bool highPrecision) { - Color c1 = new Rgba64(100, 2000, 3000, 40000); - Color c2 = new Rgba64(100, 2000, 3000, 40000); + Color c1 = Color.FromPixel(new Rgba64(100, 2000, 3000, 40000)); + Color c2 = Color.FromPixel(new Rgba64(100, 2000, 3000, 40000)); if (highPrecision) { - c1 = Color.FromPixel(c1.ToPixel()); - c2 = Color.FromPixel(c2.ToPixel()); + c1 = Color.FromPixel(c1.ToPixel()); + c2 = Color.FromPixel(c2.ToPixel()); } Assert.True(c1.Equals(c2)); @@ -43,15 +43,15 @@ public partial class ColorTests [InlineData(true)] public void Equality_WhenFalse(bool highPrecision) { - Color c1 = new Rgba64(100, 2000, 3000, 40000); - Color c2 = new Rgba64(101, 2000, 3000, 40000); - Color c3 = new Rgba64(100, 2000, 3000, 40001); + Color c1 = Color.FromPixel(new Rgba64(100, 2000, 3000, 40000)); + Color c2 = Color.FromPixel(new Rgba64(101, 2000, 3000, 40000)); + Color c3 = Color.FromPixel(new Rgba64(100, 2000, 3000, 40001)); if (highPrecision) { - c1 = Color.FromPixel(c1.ToPixel()); - c2 = Color.FromPixel(c2.ToPixel()); - c3 = Color.FromPixel(c3.ToPixel()); + c1 = Color.FromPixel(c1.ToPixel()); + c2 = Color.FromPixel(c2.ToPixel()); + c3 = Color.FromPixel(c3.ToPixel()); } Assert.False(c1.Equals(c2)); @@ -74,7 +74,7 @@ public partial class ColorTests if (highPrecision) { - color = Color.FromPixel(color.ToPixel()); + color = Color.FromPixel(color.ToPixel()); } string actual = color.ToHex(); @@ -84,22 +84,22 @@ public partial class ColorTests [Fact] public void WebSafePalette_IsCorrect() { - Rgba32[] actualPalette = Color.WebSafePalette.ToArray().Select(c => (Rgba32)c).ToArray(); + Rgba32[] actualPalette = Color.WebSafePalette.ToArray().Select(c => c.ToPixel()).ToArray(); for (int i = 0; i < ReferencePalette.WebSafeColors.Length; i++) { - Assert.Equal((Rgba32)ReferencePalette.WebSafeColors[i], actualPalette[i]); + Assert.Equal(ReferencePalette.WebSafeColors[i].ToPixel(), actualPalette[i]); } } [Fact] public void WernerPalette_IsCorrect() { - Rgba32[] actualPalette = Color.WernerPalette.ToArray().Select(c => (Rgba32)c).ToArray(); + Rgba32[] actualPalette = Color.WernerPalette.ToArray().Select(c => c.ToPixel()).ToArray(); for (int i = 0; i < ReferencePalette.WernerColors.Length; i++) { - Assert.Equal((Rgba32)ReferencePalette.WernerColors[i], actualPalette[i]); + Assert.Equal(ReferencePalette.WernerColors[i].ToPixel(), actualPalette[i]); } } @@ -108,66 +108,48 @@ public partial class ColorTests [Fact] public void ShortHex() { - Assert.Equal(new Rgb24(255, 255, 255), (Rgb24)Color.ParseHex("#fff")); - Assert.Equal(new Rgb24(255, 255, 255), (Rgb24)Color.ParseHex("fff")); - Assert.Equal(new Rgba32(0, 0, 0, 255), (Rgba32)Color.ParseHex("000f")); + Assert.Equal(new Rgb24(255, 255, 255), Color.ParseHex("#fff").ToPixel()); + Assert.Equal(new Rgb24(255, 255, 255), Color.ParseHex("fff").ToPixel()); + Assert.Equal(new Rgba32(0, 0, 0, 255), Color.ParseHex("000f").ToPixel()); } [Fact] public void TryShortHex() { Assert.True(Color.TryParseHex("#fff", out Color actual)); - Assert.Equal(new Rgb24(255, 255, 255), (Rgb24)actual); + Assert.Equal(new Rgb24(255, 255, 255), actual.ToPixel()); Assert.True(Color.TryParseHex("fff", out actual)); - Assert.Equal(new Rgb24(255, 255, 255), (Rgb24)actual); + Assert.Equal(new Rgb24(255, 255, 255), actual.ToPixel()); Assert.True(Color.TryParseHex("000f", out actual)); - Assert.Equal(new Rgba32(0, 0, 0, 255), (Rgba32)actual); + Assert.Equal(new Rgba32(0, 0, 0, 255), actual.ToPixel()); } [Fact] public void LeadingPoundIsOptional() { - Assert.Equal(new Rgb24(0, 128, 128), (Rgb24)Color.ParseHex("#008080")); - Assert.Equal(new Rgb24(0, 128, 128), (Rgb24)Color.ParseHex("008080")); + Assert.Equal(new Rgb24(0, 128, 128), Color.ParseHex("#008080").ToPixel()); + Assert.Equal(new Rgb24(0, 128, 128), Color.ParseHex("008080").ToPixel()); } [Fact] - public void ThrowsOnEmpty() - { - Assert.Throws(() => Color.ParseHex(string.Empty)); - } + public void ThrowsOnEmpty() => Assert.Throws(() => Color.ParseHex(string.Empty)); [Fact] - public void ThrowsOnInvalid() - { - Assert.Throws(() => Color.ParseHex("!")); - } + public void ThrowsOnInvalid() => Assert.Throws(() => Color.ParseHex("!")); [Fact] - public void ThrowsOnNull() - { - Assert.Throws(() => Color.ParseHex(null)); - } + public void ThrowsOnNull() => Assert.Throws(() => Color.ParseHex(null)); [Fact] - public void FalseOnEmpty() - { - Assert.False(Color.TryParseHex(string.Empty, out Color _)); - } + public void FalseOnEmpty() => Assert.False(Color.TryParseHex(string.Empty, out Color _)); [Fact] - public void FalseOnInvalid() - { - Assert.False(Color.TryParseHex("!", out Color _)); - } + public void FalseOnInvalid() => Assert.False(Color.TryParseHex("!", out Color _)); [Fact] - public void FalseOnNull() - { - Assert.False(Color.TryParseHex(null, out Color _)); - } + public void FalseOnNull() => Assert.False(Color.TryParseHex(null, out Color _)); } public class FromString @@ -177,10 +159,10 @@ public partial class ColorTests { foreach (string name in ReferencePalette.ColorNames.Keys) { - Rgba32 expected = ReferencePalette.ColorNames[name]; - Assert.Equal(expected, (Rgba32)Color.Parse(name)); - Assert.Equal(expected, (Rgba32)Color.Parse(name.ToLowerInvariant())); - Assert.Equal(expected, (Rgba32)Color.Parse(expected.ToHex())); + Rgba32 expected = ReferencePalette.ColorNames[name].ToPixel(); + Assert.Equal(expected, Color.Parse(name).ToPixel()); + Assert.Equal(expected, Color.Parse(name.ToLowerInvariant()).ToPixel()); + Assert.Equal(expected, Color.Parse(expected.ToHex()).ToPixel()); } } @@ -189,53 +171,35 @@ public partial class ColorTests { foreach (string name in ReferencePalette.ColorNames.Keys) { - Rgba32 expected = ReferencePalette.ColorNames[name]; + Rgba32 expected = ReferencePalette.ColorNames[name].ToPixel(); Assert.True(Color.TryParse(name, out Color actual)); - Assert.Equal(expected, (Rgba32)actual); + Assert.Equal(expected, actual.ToPixel()); Assert.True(Color.TryParse(name.ToLowerInvariant(), out actual)); - Assert.Equal(expected, (Rgba32)actual); + Assert.Equal(expected, actual.ToPixel()); Assert.True(Color.TryParse(expected.ToHex(), out actual)); - Assert.Equal(expected, (Rgba32)actual); + Assert.Equal(expected, actual.ToPixel()); } } [Fact] - public void ThrowsOnEmpty() - { - Assert.Throws(() => Color.Parse(string.Empty)); - } + public void ThrowsOnEmpty() => Assert.Throws(() => Color.Parse(string.Empty)); [Fact] - public void ThrowsOnInvalid() - { - Assert.Throws(() => Color.Parse("!")); - } + public void ThrowsOnInvalid() => Assert.Throws(() => Color.Parse("!")); [Fact] - public void ThrowsOnNull() - { - Assert.Throws(() => Color.Parse(null)); - } + public void ThrowsOnNull() => Assert.Throws(() => Color.Parse(null)); [Fact] - public void FalseOnEmpty() - { - Assert.False(Color.TryParse(string.Empty, out Color _)); - } + public void FalseOnEmpty() => Assert.False(Color.TryParse(string.Empty, out Color _)); [Fact] - public void FalseOnInvalid() - { - Assert.False(Color.TryParse("!", out Color _)); - } + public void FalseOnInvalid() => Assert.False(Color.TryParse("!", out Color _)); [Fact] - public void FalseOnNull() - { - Assert.False(Color.TryParse(null, out Color _)); - } + public void FalseOnNull() => Assert.False(Color.TryParse(null, out Color _)); } } diff --git a/tests/ImageSharp.Tests/Color/RgbaDouble.cs b/tests/ImageSharp.Tests/Color/RgbaDouble.cs new file mode 100644 index 000000000..476ff0336 --- /dev/null +++ b/tests/ImageSharp.Tests/Color/RgbaDouble.cs @@ -0,0 +1,180 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests; + +/// +/// Unpacked pixel type containing four 64-bit floating-point values typically ranging from 0 to 1. +/// The color components are stored in red, green, blue, and alpha order. +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +/// +/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, +/// as it avoids the need to create new values for modification operations. +/// +/// +/// Initializes a new instance of the struct. +/// +/// The red component. +/// The green component. +/// The blue component. +/// The alpha component. +[StructLayout(LayoutKind.Sequential)] +[method: MethodImpl(InliningOptions.ShortMethod)] +public partial struct RgbaDouble(double r, double g, double b, double a = 1) : IPixel +{ + /// + /// Gets or sets the red component. + /// + public double R = r; + + /// + /// Gets or sets the green component. + /// + public double G = g; + + /// + /// Gets or sets the blue component. + /// + public double B = b; + + /// + /// Gets or sets the alpha component. + /// + public double A = a; + + private const float MaxBytes = byte.MaxValue; + private static readonly Vector4 Max = new(MaxBytes); + private static readonly Vector4 Half = new(0.5F); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(RgbaDouble left, RgbaDouble right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(RgbaDouble left, RgbaDouble right) => !left.Equals(right); + + /// + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 64, 64, 64, 64), PixelAlphaRepresentation.Unassociated); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; + this.A = vector.W; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly Vector4 ToVector4() => new((float)this.R, (float)this.G, (float)this.B, (float)this.A); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object obj) => obj is RgbaDouble other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly bool Equals(RgbaDouble other) => + this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B) + && this.A.Equals(other.A); + + /// + public override readonly string ToString() => FormattableString.Invariant($"RgbaDouble({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##}, {this.A:#0.##})"); + + /// + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 533f0c6de..88f4cde7a 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -119,7 +119,7 @@ public class DrawImageTests public void WorksWithDifferentLocations(TestImageProvider provider, int x, int y) { using Image background = provider.GetImage(); - using Image overlay = new(50, 50, Color.Black.ToRgba32()); + using Image overlay = new(50, 50, Color.Black.ToPixel()); background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); @@ -144,7 +144,7 @@ public class DrawImageTests public void WorksWithDifferentBounds(TestImageProvider provider, int width, int height) { using Image background = provider.GetImage(); - using Image overlay = new(50, 50, Color.Black.ToRgba32()); + using Image overlay = new(50, 50, Color.Black.ToPixel()); background.Mutate(c => c.DrawImage(overlay, new Rectangle(0, 0, width, height), PixelColorBlendingMode.Normal, 1F)); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index bc277bf48..800b1fb4b 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -581,7 +581,7 @@ public partial class PngDecoderTests using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata metadata = image.Metadata.GetPngMetadata(); Assert.NotNull(metadata.ColorTable); - Assert.Contains(metadata.ColorTable.Value.ToArray(), x => x.ToRgba32().A < 255); + Assert.Contains(metadata.ColorTable.Value.ToArray(), x => x.ToPixel().A < 255); } // https://github.com/SixLabors/ImageSharp/issues/2209 @@ -594,7 +594,7 @@ public partial class PngDecoderTests ImageInfo imageInfo = Image.Identify(stream); PngMetadata metadata = imageInfo.Metadata.GetPngMetadata(); Assert.NotNull(metadata.ColorTable); - Assert.Contains(metadata.ColorTable.Value.ToArray(), x => x.ToRgba32().A < 255); + Assert.Contains(metadata.ColorTable.Value.ToArray(), x => x.ToPixel().A < 255); } // https://github.com/SixLabors/ImageSharp/issues/410 diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index e70854b08..477d88e9a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -391,7 +391,7 @@ public partial class PngEncoderTests TransparentColorMode = PngTransparentColorMode.Clear, ColorType = colorType }; - Rgba32 rgba32 = Color.Blue; + Rgba32 rgba32 = Color.Blue.ToPixel(); image.ProcessPixelRows(accessor => { for (int y = 0; y < image.Height; y++) @@ -418,7 +418,7 @@ public partial class PngEncoderTests // assert memStream.Position = 0; using Image actual = Image.Load(memStream); - Rgba32 expectedColor = Color.Blue; + Rgba32 expectedColor = Color.Blue.ToPixel(); if (colorType is PngColorType.Grayscale or PngColorType.GrayscaleWithAlpha) { byte luminance = ColorNumerics.Get8BitBT709Luminance(expectedColor.R, expectedColor.G, expectedColor.B); @@ -427,13 +427,14 @@ public partial class PngEncoderTests actual.ProcessPixelRows(accessor => { + Rgba32 transparent = Color.Transparent.ToPixel(); for (int y = 0; y < accessor.Height; y++) { Span rowSpan = accessor.GetRowSpan(y); if (y > 25) { - expectedColor = Color.Transparent; + expectedColor = transparent; } for (int x = 0; x < accessor.Width; x++) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index b17440383..3582dc75a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -47,7 +47,7 @@ public abstract class PhotometricInterpretationTestBase using (var image = new Image(resultWidth, resultHeight)) { - image.Mutate(x => x.BackgroundColor(DefaultColor)); + image.Mutate(x => x.BackgroundColor(Color.FromPixel(DefaultColor))); Buffer2D pixels = image.GetRootFramePixelBuffer(); decodeAction(pixels); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs index b74093fcc..dce6ebc38 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs @@ -70,9 +70,9 @@ public class TiffEncoderMultiframeTests : TiffEncoderBaseTester using Image image = provider.GetImage(); Assert.Equal(1, image.Frames.Count); - using var image1 = new Image(image.Width, image.Height, Color.Green.ToRgba32()); + using var image1 = new Image(image.Width, image.Height, Color.Green.ToPixel()); - using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToRgba32()); + using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToPixel()); image.Frames.AddFrame(image1.Frames.RootFrame); image.Frames.AddFrame(image2.Frames.RootFrame); @@ -97,8 +97,8 @@ public class TiffEncoderMultiframeTests : TiffEncoderBaseTester ImageFrame frame1 = output.Frames[1]; ImageFrame frame2 = output.Frames[2]; - Assert.Equal(Color.Green.ToRgba32(), frame1[10, 10]); - Assert.Equal(Color.Yellow.ToRgba32(), frame2[10, 10]); + Assert.Equal(Color.Green.ToPixel(), frame1[10, 10]); + Assert.Equal(Color.Yellow.ToPixel(), frame2[10, 10]); Assert.Equal(TiffCompression.Deflate, frame1.Metadata.GetTiffMetadata().Compression); Assert.Equal(TiffCompression.Deflate, frame1.Metadata.GetTiffMetadata().Compression); @@ -122,11 +122,11 @@ public class TiffEncoderMultiframeTests : TiffEncoderBaseTester { using Image image = provider.GetImage(); - using var image0 = new Image(image.Width, image.Height, Color.Red.ToRgba32()); + using var image0 = new Image(image.Width, image.Height, Color.Red.ToPixel()); - using var image1 = new Image(image.Width, image.Height, Color.Green.ToRgba32()); + using var image1 = new Image(image.Width, image.Height, Color.Green.ToPixel()); - using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToRgba32()); + using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToPixel()); image.Frames.AddFrame(image0.Frames.RootFrame); image.Frames.AddFrame(image1.Frames.RootFrame); @@ -154,9 +154,9 @@ public class TiffEncoderMultiframeTests : TiffEncoderBaseTester ImageFrame frame1 = output.Frames[1]; ImageFrame frame2 = output.Frames[2]; - Assert.Equal(Color.Red.ToRgba32(), frame0[10, 10]); - Assert.Equal(Color.Green.ToRgba32(), frame1[10, 10]); - Assert.Equal(Color.Yellow.ToRgba32(), frame2[10, 10]); + Assert.Equal(Color.Red.ToPixel(), frame0[10, 10]); + Assert.Equal(Color.Green.ToPixel(), frame1[10, 10]); + Assert.Equal(Color.Yellow.ToPixel(), frame2[10, 10]); Assert.Equal(TiffCompression.Lzw, frame0.Metadata.GetTiffMetadata().Compression); Assert.Equal(TiffCompression.Lzw, frame1.Metadata.GetTiffMetadata().Compression); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index 62d1ef4db..71c9e07fc 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -237,7 +237,7 @@ public abstract partial class ImageFrameCollectionTests using (this.Image.Frames.CreateFrame(Color.HotPink)) { Assert.Equal(2, this.Image.Frames.Count); - this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink); + this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink.ToPixel()); } } diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index f9ff6ba69..a42dcc481 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; @@ -23,13 +22,13 @@ public abstract partial class ImageFrameCollectionTests this.Image.Configuration, this.Image.Width, this.Image.Height, - Color.Blue)) + Color.Blue.ToPixel())) { this.Collection.AddFrame(sourceImage.Frames.RootFrame); } Rgba32[] expectedAllBlue = - Enumerable.Repeat((Rgba32)Color.Blue, this.Image.Width * this.Image.Height).ToArray(); + Enumerable.Repeat(Color.Blue.ToPixel(), this.Image.Width * this.Image.Height).ToArray(); Assert.Equal(2, this.Collection.Count); ImageFrame actualFrame = (ImageFrame)this.Collection[1]; @@ -44,13 +43,13 @@ public abstract partial class ImageFrameCollectionTests this.Image.Configuration, this.Image.Width, this.Image.Height, - Color.Blue)) + Color.Blue.ToPixel())) { this.Collection.InsertFrame(0, sourceImage.Frames.RootFrame); } Rgba32[] expectedAllBlue = - Enumerable.Repeat((Rgba32)Color.Blue, this.Image.Width * this.Image.Height).ToArray(); + Enumerable.Repeat(Color.Blue.ToPixel(), this.Image.Width * this.Image.Height).ToArray(); Assert.Equal(2, this.Collection.Count); ImageFrame actualFrame = (ImageFrame)this.Collection[0]; @@ -177,7 +176,7 @@ public abstract partial class ImageFrameCollectionTests ImageFrame frame = (ImageFrame)this.Image.Frames[1]; - frame.ComparePixelBufferTo(Color.HotPink); + frame.ComparePixelBufferTo(Color.HotPink.ToPixel()); } [Fact] @@ -279,7 +278,7 @@ public abstract partial class ImageFrameCollectionTests { using Image source = provider.GetImage(); using Image dest = new(source.Configuration, source.Width, source.Height); - + // Giphy.gif has 5 frames ImportFrameAs(source.Frames, dest.Frames, 0); ImportFrameAs(source.Frames, dest.Frames, 1); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameTests.cs b/tests/ImageSharp.Tests/Image/ImageFrameTests.cs index 3b9779ea4..e09ef487a 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameTests.cs @@ -35,9 +35,9 @@ public class ImageFrameTests ImageFrame frame = image.Frames.RootFrame; Rgba32 val = frame[3, 4]; Assert.Equal(default(Rgba32), val); - frame[3, 4] = Color.Red; + frame[3, 4] = Color.Red.ToPixel(); val = frame[3, 4]; - Assert.Equal(Color.Red.ToRgba32(), val); + Assert.Equal(Color.Red.ToPixel(), val); } public static TheoryData OutOfRangeData = new TheoryData() diff --git a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs index 238096be4..5762264d2 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs @@ -15,19 +15,17 @@ public partial class ImageTests [ValidateDisposedMemoryAllocations] public void FromPixels(bool useSpan) { - Rgba32[] data = { Color.Black, Color.White, Color.White, Color.Black, }; + Rgba32[] data = { Color.Black.ToPixel(), Color.White.ToPixel(), Color.White.ToPixel(), Color.Black.ToPixel(), }; - using (Image img = useSpan - ? Image.LoadPixelData(data.AsSpan(), 2, 2) - : Image.LoadPixelData(data, 2, 2)) - { - Assert.NotNull(img); - Assert.Equal(Color.Black, (Color)img[0, 0]); - Assert.Equal(Color.White, (Color)img[0, 1]); + using Image img = useSpan + ? Image.LoadPixelData(data.AsSpan(), 2, 2) + : Image.LoadPixelData(data, 2, 2); + Assert.NotNull(img); + Assert.Equal(Color.Black, Color.FromPixel(img[0, 0])); + Assert.Equal(Color.White, Color.FromPixel(img[0, 1])); - Assert.Equal(Color.White, (Color)img[1, 0]); - Assert.Equal(Color.Black, (Color)img[1, 1]); - } + Assert.Equal(Color.White, Color.FromPixel(img[1, 0])); + Assert.Equal(Color.Black, Color.FromPixel(img[1, 1])); } [Theory] @@ -36,23 +34,22 @@ public partial class ImageTests public void FromBytes(bool useSpan) { byte[] data = - { - 0, 0, 0, 255, // 0,0 - 255, 255, 255, 255, // 0,1 - 255, 255, 255, 255, // 1,0 - 0, 0, 0, 255, // 1,1 - }; - using (Image img = useSpan - ? Image.LoadPixelData(data.AsSpan(), 2, 2) - : Image.LoadPixelData(data, 2, 2)) { - Assert.NotNull(img); - Assert.Equal(Color.Black, (Color)img[0, 0]); - Assert.Equal(Color.White, (Color)img[0, 1]); + 0, 0, 0, 255, // 0,0 + 255, 255, 255, 255, // 0,1 + 255, 255, 255, 255, // 1,0 + 0, 0, 0, 255, // 1,1 + }; + + using Image img = useSpan + ? Image.LoadPixelData(data.AsSpan(), 2, 2) + : Image.LoadPixelData(data, 2, 2); + Assert.NotNull(img); + Assert.Equal(Color.Black, Color.FromPixel(img[0, 0])); + Assert.Equal(Color.White, Color.FromPixel(img[0, 1])); - Assert.Equal(Color.White, (Color)img[1, 0]); - Assert.Equal(Color.Black, (Color)img[1, 1]); - } + Assert.Equal(Color.White, Color.FromPixel(img[1, 0])); + Assert.Equal(Color.Black, Color.FromPixel(img[1, 1])); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 9aaefa41e..f22a55aae 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -154,8 +154,8 @@ public partial class ImageTests using (var memoryManager = new BitmapMemoryManager(bmp)) { Memory memory = memoryManager.Memory; - Bgra32 bg = Color.Red; - Bgra32 fg = Color.Green; + Bgra32 bg = Color.Red.ToPixel(); + Bgra32 fg = Color.Green.ToPixel(); using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { @@ -198,8 +198,8 @@ public partial class ImageTests using (var bmp = new Bitmap(51, 23)) { var memoryManager = new BitmapMemoryManager(bmp); - Bgra32 bg = Color.Red; - Bgra32 fg = Color.Green; + Bgra32 bg = Color.Red.ToPixel(); + Bgra32 fg = Color.Green.ToPixel(); using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) { @@ -258,8 +258,8 @@ public partial class ImageTests { Memory pixelMemory = memoryManager.Memory; Memory byteMemory = new CastMemoryManager(pixelMemory).Memory; - Bgra32 bg = Color.Red; - Bgra32 fg = Color.Green; + Bgra32 bg = Color.Red.ToPixel(); + Bgra32 fg = Color.Green.ToPixel(); using (var image = Image.WrapMemory(byteMemory, bmp.Width, bmp.Height)) { @@ -355,8 +355,8 @@ public partial class ImageTests using (var memoryManager = new BitmapMemoryManager(bmp)) { Memory pixelMemory = memoryManager.Memory; - Bgra32 bg = Color.Red; - Bgra32 fg = Color.Green; + Bgra32 bg = Color.Red.ToPixel(); + Bgra32 fg = Color.Green.ToPixel(); fixed (void* p = pixelMemory.Span) { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index ca51f7f5c..ac91ea948 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -61,7 +61,7 @@ public partial class ImageTests public void Configuration_Width_Height_BackgroundColor() { Configuration configuration = Configuration.Default.Clone(); - Rgba32 color = Color.Aquamarine; + Rgba32 color = Color.Aquamarine.ToPixel(); using (Image image = new(configuration, 11, 23, color)) { @@ -116,9 +116,9 @@ public partial class ImageTests using Image image = new(this.configuration, 10, 10); Rgba32 val = image[3, 4]; Assert.Equal(default(Rgba32), val); - image[3, 4] = Color.Red; + image[3, 4] = Color.Red.ToPixel(); val = image[3, 4]; - Assert.Equal(Color.Red.ToRgba32(), val); + Assert.Equal(Color.Red.ToPixel(), val); } public static TheoryData OutOfRangeData = new() diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 73324eccd..576d14396 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index 518aa203d..ef99db498 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -116,8 +115,12 @@ public class A8Tests { PixelTypeInfo info = A8.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(1, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs index dbe02e986..de6f519e1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -151,8 +150,15 @@ public class Abgr32Tests { PixelTypeInfo info = Abgr32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index fd8ee144b..29349b852 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -149,8 +148,15 @@ public class Argb32Tests { PixelTypeInfo info = Argb32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 78c58e5c2..519aeb6c5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -134,8 +133,14 @@ public class Bgr24Tests { PixelTypeInfo info = Bgr24.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(3, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 68f953225..16e225a9b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -256,8 +255,14 @@ public class Bgr565Tests { PixelTypeInfo info = Bgr565.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(3, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(5, componentInfo.GetComponentPrecision(0)); + Assert.Equal(6, componentInfo.GetComponentPrecision(1)); + Assert.Equal(5, componentInfo.GetComponentPrecision(2)); + Assert.Equal(6, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index e92fcf1d3..c5b4325c9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -151,8 +150,15 @@ public class Bgra32Tests { PixelTypeInfo info = Bgra32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index 1af84c0c8..f2e9a2291 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -252,8 +251,15 @@ public class Bgra4444Tests { PixelTypeInfo info = Bgra4444.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(4, componentInfo.GetComponentPrecision(0)); + Assert.Equal(4, componentInfo.GetComponentPrecision(1)); + Assert.Equal(4, componentInfo.GetComponentPrecision(2)); + Assert.Equal(4, componentInfo.GetComponentPrecision(3)); + Assert.Equal(4, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index a0926d4dd..03b4a41c6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -278,8 +277,15 @@ public class Bgra5551Tests { PixelTypeInfo info = Bgra5551.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(5, componentInfo.GetComponentPrecision(0)); + Assert.Equal(5, componentInfo.GetComponentPrecision(1)); + Assert.Equal(5, componentInfo.GetComponentPrecision(2)); + Assert.Equal(1, componentInfo.GetComponentPrecision(3)); + Assert.Equal(5, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 5b456459e..1434d7d93 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -245,8 +244,15 @@ public class Byte4Tests { PixelTypeInfo info = Byte4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index 9c546e2f0..dbd1f6917 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -74,8 +73,12 @@ public class HalfSingleTests { PixelTypeInfo info = HalfSingle.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(1, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index 4f84d6087..710135ed6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -96,8 +95,13 @@ public class HalfVector2Tests { PixelTypeInfo info = HalfVector2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index 33f0173d2..3e3f6a2ca 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -87,8 +86,15 @@ public class HalfVector4Tests { PixelTypeInfo info = HalfVector4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Half, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetComponentPrecision(2)); + Assert.Equal(16, componentInfo.GetComponentPrecision(3)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 0c0cdbe3b..3a8239923 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -166,8 +165,12 @@ public class L16Tests { PixelTypeInfo info = L16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(1, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index d8bce0640..631a0e234 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -261,9 +260,13 @@ public class L8Tests { PixelTypeInfo info = L8.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(1, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(1, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index 46e7457a5..9fa0e3263 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -265,9 +264,14 @@ public class La16Tests { PixelTypeInfo info = La16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index 65aa8b1c9..089c2076b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -172,8 +171,13 @@ public class La32Tests { PixelTypeInfo info = La32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 999ddb934..9f24346a0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -88,8 +87,13 @@ public class NormalizedByte2Tests { PixelTypeInfo info = NormalizedByte2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.SByte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 64a996453..63bb9d5f5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -238,8 +237,15 @@ public class NormalizedByte4Tests { PixelTypeInfo info = NormalizedByte4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.SByte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index e6d4ae98d..f262ce177 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -92,8 +91,13 @@ public class NormalizedShort2Tests { PixelTypeInfo info = NormalizedShort2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index cd684311a..05058ae4d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -239,8 +238,15 @@ public class NormalizedShort4Tests { PixelTypeInfo info = NormalizedShort4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetComponentPrecision(2)); + Assert.Equal(16, componentInfo.GetComponentPrecision(3)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index 68cdf2d63..924e94d92 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -43,15 +43,15 @@ public class PixelBlenderTests public static TheoryData ColorBlendingExpectedResults = new() { - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Normal, Color.MidnightBlue }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Normal, Color.MidnightBlue.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) }, }; [Theory] @@ -67,18 +67,18 @@ public class PixelBlenderTests public static TheoryData AlphaCompositionExpectedResults = new() { - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, Color.MistyRose }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.DestAtop, Color.MistyRose }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.DestIn, Color.MistyRose }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Color.MistyRose }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.Src, Color.MidnightBlue }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.SrcAtop, Color.MidnightBlue }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.SrcIn, Color.MidnightBlue }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) }, - { Color.MistyRose, Color.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Color.MidnightBlue }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.Dest, Color.MistyRose.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.DestAtop, Color.MistyRose.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.DestIn, Color.MistyRose.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.DestOver, Color.MistyRose.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.Src, Color.MidnightBlue.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.SrcAtop, Color.MidnightBlue.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.SrcIn, Color.MidnightBlue.ToPixel() }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) }, + { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelAlphaCompositionMode.SrcOver, Color.MidnightBlue.ToPixel() }, }; [Theory] diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index bf3ff6416..9ab1ec963 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.Formats; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -90,8 +89,13 @@ public class Rg32Tests { PixelTypeInfo info = Rg32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index c69c757b5..ab4269d00 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -137,8 +136,14 @@ public class Rgb24Tests { PixelTypeInfo info = Rgb24.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(3, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index dda5af9fb..faf9bdb3c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -82,8 +81,14 @@ public class Rgb48Tests { PixelTypeInfo info = Rgb48.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(3, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(3, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetComponentPrecision(2)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 5b0e4c287..7cf8ced45 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -253,8 +252,15 @@ public class Rgba1010102Tests { PixelTypeInfo info = Rgba1010102.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(10, componentInfo.GetComponentPrecision(0)); + Assert.Equal(10, componentInfo.GetComponentPrecision(1)); + Assert.Equal(10, componentInfo.GetComponentPrecision(2)); + Assert.Equal(2, componentInfo.GetComponentPrecision(3)); + Assert.Equal(10, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index d1ad4c304..9b96aeb7b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -313,8 +312,15 @@ public class Rgba32Tests { PixelTypeInfo info = Rgba32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Byte, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(8, componentInfo.GetComponentPrecision(0)); + Assert.Equal(8, componentInfo.GetComponentPrecision(1)); + Assert.Equal(8, componentInfo.GetComponentPrecision(2)); + Assert.Equal(8, componentInfo.GetComponentPrecision(3)); + Assert.Equal(8, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 3dc580460..49336400c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -315,8 +314,15 @@ public class Rgba64Tests { PixelTypeInfo info = Rgba64.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.UShort, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetComponentPrecision(2)); + Assert.Equal(16, componentInfo.GetComponentPrecision(3)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index 968e274e2..28ee5ace5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -211,8 +210,15 @@ public class RgbaVectorTests { PixelTypeInfo info = RgbaVector.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Float, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(32, componentInfo.GetComponentPrecision(0)); + Assert.Equal(32, componentInfo.GetComponentPrecision(1)); + Assert.Equal(32, componentInfo.GetComponentPrecision(2)); + Assert.Equal(32, componentInfo.GetComponentPrecision(3)); + Assert.Equal(32, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index 7afe61b02..d709b7605 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -167,8 +166,13 @@ public class Short2Tests { PixelTypeInfo info = Short2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(2, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(2, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index bdde5cd8f..2b5760342 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -220,8 +219,15 @@ public class Short4Tests { PixelTypeInfo info = Short4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); - Assert.Equal(4, info.ComponentCount); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelComponentPrecision.Short, info.MaxComponentPrecision); + + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + Assert.Equal(16, componentInfo.GetComponentPrecision(0)); + Assert.Equal(16, componentInfo.GetComponentPrecision(1)); + Assert.Equal(16, componentInfo.GetComponentPrecision(2)); + Assert.Equal(16, componentInfo.GetComponentPrecision(3)); + Assert.Equal(16, componentInfo.GetMaximumComponentPrecision()); } } diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index d6b1f1f98..ccb79debd 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -14,7 +14,7 @@ public class WuQuantizerTests Configuration config = Configuration.Default; var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); - using var image = new Image(config, 1, 1, Color.Black); + using var image = new Image(config, 1, 1, Color.Black.ToPixel()); ImageFrame frame = image.Frames.RootFrame; using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); @@ -24,7 +24,7 @@ public class WuQuantizerTests Assert.Equal(1, result.Width); Assert.Equal(1, result.Height); - Assert.Equal(Color.Black, (Color)result.Palette.Span[0]); + Assert.Equal(Color.Black, Color.FromPixel(result.Palette.Span[0])); Assert.Equal(0, result.DangerousGetRowSpan(0)[0]); } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index a4232a968..495f5f9f6 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -263,7 +263,7 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public struct TestPixelForAgnosticDecode : IPixel { - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(2, PixelComponentPrecision.Byte, PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.None); public PixelOperations CreatePixelOperations() => new(); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 8c5101013..d78edf5bc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -51,7 +51,7 @@ public abstract partial class TestImageProvider : IXunitSerializable public override Image GetImage() { Image image = base.GetImage(); - Color color = new Rgba32(this.r, this.g, this.b, this.a); + Color color = Color.FromPixel(new Rgba32(this.r, this.g, this.b, this.a)); image.GetRootFramePixelBuffer().FastMemoryGroup.Fill(color.ToPixel()); return image; From 1d352513e5436014894d48cbb239516b39bceae6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 5 Jan 2024 18:52:39 +1000 Subject: [PATCH 037/220] Add PixelColorType --- src/ImageSharp/PixelFormats/PixelColorType.cs | 46 +++++++++++++++++++ .../PixelFormats/PixelImplementations/A8.cs | 6 ++- .../PixelImplementations/Abgr32.cs | 6 ++- .../PixelImplementations/Argb32.cs | 6 ++- .../PixelImplementations/Bgr24.cs | 6 ++- .../PixelImplementations/Bgr565.cs | 6 ++- .../PixelImplementations/Bgra32.cs | 6 ++- .../PixelImplementations/Bgra4444.cs | 6 ++- .../PixelImplementations/Bgra5551.cs | 6 ++- .../PixelImplementations/Byte4.cs | 6 ++- .../PixelImplementations/HalfSingle.cs | 6 ++- .../PixelImplementations/HalfVector2.cs | 6 ++- .../PixelImplementations/HalfVector4.cs | 6 ++- .../PixelFormats/PixelImplementations/L16.cs | 6 ++- .../PixelFormats/PixelImplementations/L8.cs | 6 ++- .../PixelFormats/PixelImplementations/La16.cs | 6 ++- .../PixelFormats/PixelImplementations/La32.cs | 6 ++- .../PixelImplementations/NormalizedByte2.cs | 6 ++- .../PixelImplementations/NormalizedByte4.cs | 6 ++- .../PixelImplementations/NormalizedShort2.cs | 6 ++- .../PixelImplementations/NormalizedShort4.cs | 6 ++- .../PixelFormats/PixelImplementations/Rg32.cs | 6 ++- .../PixelImplementations/Rgb24.cs | 6 ++- .../PixelImplementations/Rgb48.cs | 6 ++- .../PixelImplementations/Rgba1010102.cs | 6 ++- .../PixelImplementations/Rgba32.cs | 6 ++- .../PixelImplementations/Rgba64.cs | 6 ++- .../PixelImplementations/RgbaVector.cs | 6 ++- .../PixelImplementations/Short2.cs | 6 ++- .../PixelImplementations/Short4.cs | 6 ++- src/ImageSharp/PixelFormats/PixelTypeInfo.cs | 10 +++- tests/ImageSharp.Tests/Color/RgbaDouble.cs | 6 ++- .../ImageSharp.Tests/PixelFormats/A8Tests.cs | 1 + .../PixelFormats/Abgr32Tests.cs | 1 + .../PixelFormats/Argb32Tests.cs | 1 + .../PixelFormats/Bgr24Tests.cs | 1 + .../PixelFormats/Bgr565Tests.cs | 1 + .../PixelFormats/Bgra32Tests.cs | 1 + .../PixelFormats/Bgra4444Tests.cs | 1 + .../PixelFormats/Bgra5551Tests.cs | 1 + .../PixelFormats/Byte4Tests.cs | 1 + .../PixelFormats/HalfSingleTests.cs | 1 + .../PixelFormats/HalfVector2Tests.cs | 1 + .../PixelFormats/HalfVector4Tests.cs | 1 + .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 1 + .../ImageSharp.Tests/PixelFormats/L8Tests.cs | 1 + .../PixelFormats/La16Tests.cs | 1 + .../PixelFormats/La32Tests.cs | 1 + .../PixelFormats/NormalizedByte2Tests.cs | 1 + .../PixelFormats/NormalizedByte4Tests.cs | 1 + .../PixelFormats/NormalizedShort2Tests.cs | 1 + .../PixelFormats/NormalizedShort4Tests.cs | 1 + .../PixelFormats/Rg32Tests.cs | 1 + .../PixelFormats/Rgb24Tests.cs | 1 + .../PixelFormats/Rgb48Tests.cs | 1 + .../PixelFormats/Rgba1010102Tests.cs | 1 + .../PixelFormats/Rgba32Tests.cs | 1 + .../PixelFormats/Rgba64Tests.cs | 1 + .../PixelFormats/RgbaVectorTests.cs | 1 + .../PixelFormats/Short2Tests.cs | 1 + .../PixelFormats/Short4Tests.cs | 1 + tests/ImageSharp.Tests/TestFormat.cs | 6 ++- 62 files changed, 239 insertions(+), 32 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelColorType.cs diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs new file mode 100644 index 000000000..9ac2c308c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Represents the color type and format of a pixel. +/// +[Flags] +public enum PixelColorType +{ + /// + /// Represents the Red component of the color. + /// + Red = 1 << 0, + + /// + /// Represents the Green component of the color. + /// + Green = 1 << 1, + + /// + /// Represents the Blue component of the color. + /// + Blue = 1 << 2, + + /// + /// Represents the Alpha component of the color for transparency. + /// + Alpha = 1 << 3, + + /// + /// Indicates that the color is in grayscale. + /// + Grayscale = 1 << 4, + + /// + /// Indicates that the color is in RGB (Red, Green, Blue) format. + /// + RGB = Red | Green | Blue | (1 << 5), + + /// + /// Indicates that the color is in BGR (Blue, Green, Red) format. + /// + BGR = Blue | Green | Red | (1 << 6) +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index abae4f246..23dae82ab 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -56,7 +56,11 @@ public partial struct A8 : IPixel, IPackedVector public static bool operator !=(A8 left, A8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 8), + PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index e1399f05c..742f27cc0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -168,7 +168,11 @@ public partial struct Abgr32 : IPixel, IPackedVector public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.BGR, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 2b7bcf913..7a8ee2a63 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -168,7 +168,11 @@ public partial struct Argb32 : IPixel, IPackedVector public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.RGB, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index c9c324680..bdf7d1a7e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -72,7 +72,11 @@ public partial struct Bgr24 : IPixel public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 8, 8, 8), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 8, 8, 8), + PixelColorType.BGR, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index d020cf025..0874eb825 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -60,7 +60,11 @@ public partial struct Bgr565 : IPixel, IPackedVector public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 5, 6, 5), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 5, 6, 5), + PixelColorType.BGR, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index b3a96523e..f50846357 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -121,7 +121,11 @@ public partial struct Bgra32 : IPixel, IPackedVector public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 510d6666f..4bb3f8a0e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -58,7 +58,11 @@ public partial struct Bgra4444 : IPixel, IPackedVector public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 4, 4, 4, 4), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 4, 4, 4, 4), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 68dbd9544..d57545dee 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -61,7 +61,11 @@ public partial struct Bgra5551 : IPixel, IPackedVector public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 5, 5, 5, 1), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 5, 5, 5, 1), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index fbe7a0c52..d8f1dd0ac 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -61,7 +61,11 @@ public partial struct Byte4 : IPixel, IPackedVector public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index aca64ae7a..01ae9fc5f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -46,7 +46,11 @@ public partial struct HalfSingle : IPixel, IPackedVector public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 16), + PixelColorType.Red, + PixelAlphaRepresentation.None); /// public PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 5a25d523b..d591dd855 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -53,7 +53,11 @@ public partial struct HalfVector2 : IPixel, IPackedVector public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 7ad7b07f4..ca6bff230 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -58,7 +58,11 @@ public partial struct HalfVector4 : IPixel, IPackedVector public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 0c58fb1fd..8522da3fb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -48,7 +48,11 @@ public partial struct L16 : IPixel, IPackedVector public static bool operator !=(L16 left, L16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 16), + PixelColorType.Grayscale, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 2cd8b20e7..706fc1101 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -49,7 +49,11 @@ public partial struct L8 : IPixel, IPackedVector public static bool operator !=(L8 left, L8 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(1, 8), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 8), + PixelColorType.Grayscale, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index b811df4b4..e30673153 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -72,7 +72,11 @@ public partial struct La16 : IPixel, IPackedVector public static bool operator !=(La16 left, La16 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Grayscale | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index e6a63f5ec..867bfacd3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -74,7 +74,11 @@ public partial struct La32 : IPixel, IPackedVector public static bool operator !=(La32 left, La32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Grayscale | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index f7427e65c..c5eb2c2de 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -61,7 +61,11 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index beeae655a..6cf92688e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -63,7 +63,11 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 8152c89d3..03fa2737e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -62,7 +62,11 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 3f37cb80f..89ba2a23b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -64,7 +64,11 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 594bdd9a1..8f2e19840 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -58,7 +58,11 @@ public partial struct Rg32 : IPixel, IPackedVector public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 8d0c7cd48..008de06bf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -91,7 +91,11 @@ public partial struct Rgb24 : IPixel public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 8, 8, 8), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 8, 8, 8), + PixelColorType.RGB, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index f7aac32f4..ae4d6dc01 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -70,7 +70,11 @@ public partial struct Rgb48 : IPixel public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(3, 16, 16, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 16, 16, 16), + PixelColorType.RGB, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 84482a7aa..ac1eedb07 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -61,7 +61,11 @@ public partial struct Rgba1010102 : IPixel, IPackedVector public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 10, 10, 10, 2), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 10, 10, 10, 2), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index f391e6474..8b2a078db 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -271,7 +271,11 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 8, 8, 8, 8), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 47062779b..b71ec5f35 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -191,7 +191,11 @@ public partial struct Rgba64 : IPixel, IPackedVector public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index ce1c6d572..cd61b13b2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -97,7 +97,11 @@ public partial struct RgbaVector : IPixel public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 32, 32, 32, 32), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 32, 32, 32, 32), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 786c98ee4..c8819337e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -65,7 +65,11 @@ public partial struct Short2 : IPixel, IPackedVector public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 16, 16), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 890e66299..cfd9ce0bf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -67,7 +67,11 @@ public partial struct Short4 : IPixel, IPackedVector public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 16, 16, 16, 16), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); diff --git a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs index e209b21e1..81c70a18f 100644 --- a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -23,10 +23,15 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) public int BitsPerPixel { get; init; } = bitsPerPixel; /// - /// Gets the maximum precision of components within the pixel. + /// Gets the component bit depth and padding within the pixel. /// public PixelComponentInfo? ComponentInfo { get; init; } + /// + /// Gets the pixel color type. + /// + public PixelColorType? ColorType { get; init; } + /// /// Gets the pixel alpha transparency behavior. /// means unknown, unspecified. @@ -38,16 +43,19 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// /// The type of pixel format. /// The pixel component info. + /// The pixel color type. /// The pixel alpha representation. /// The . public static PixelTypeInfo Create( PixelComponentInfo info, + PixelColorType colorType, PixelAlphaRepresentation alphaRepresentation) where TPixel : unmanaged, IPixel => new() { BitsPerPixel = Unsafe.SizeOf() * 8, ComponentInfo = info, + ColorType = colorType, AlphaRepresentation = alphaRepresentation }; } diff --git a/tests/ImageSharp.Tests/Color/RgbaDouble.cs b/tests/ImageSharp.Tests/Color/RgbaDouble.cs index 476ff0336..fdd736930 100644 --- a/tests/ImageSharp.Tests/Color/RgbaDouble.cs +++ b/tests/ImageSharp.Tests/Color/RgbaDouble.cs @@ -77,7 +77,11 @@ public partial struct RgbaDouble(double r, double g, double b, double a = 1) : I public static bool operator !=(RgbaDouble left, RgbaDouble right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(4, 64, 64, 64, 64), PixelAlphaRepresentation.Unassociated); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 64, 64, 64, 64), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// public readonly PixelOperations CreatePixelOperations() => new(); diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index ef99db498..b7a86d488 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -116,6 +116,7 @@ public class A8Tests PixelTypeInfo info = A8.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs index de6f519e1..7f584b1bf 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -151,6 +151,7 @@ public class Abgr32Tests PixelTypeInfo info = Abgr32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Alpha | PixelColorType.BGR, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index 29349b852..42b940845 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -149,6 +149,7 @@ public class Argb32Tests PixelTypeInfo info = Argb32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Alpha | PixelColorType.RGB, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 519aeb6c5..239e88396 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -134,6 +134,7 @@ public class Bgr24Tests PixelTypeInfo info = Bgr24.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(3, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 16e225a9b..2ad4f4cf7 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -256,6 +256,7 @@ public class Bgr565Tests PixelTypeInfo info = Bgr565.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(3, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index c5b4325c9..df8d1199f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -151,6 +151,7 @@ public class Bgra32Tests PixelTypeInfo info = Bgra32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index f2e9a2291..ef587f301 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -252,6 +252,7 @@ public class Bgra4444Tests PixelTypeInfo info = Bgra4444.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index 03b4a41c6..5140cfda1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -278,6 +278,7 @@ public class Bgra5551Tests PixelTypeInfo info = Bgra5551.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 1434d7d93..a61318db7 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -245,6 +245,7 @@ public class Byte4Tests PixelTypeInfo info = Byte4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index dbd1f6917..3e84da723 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -74,6 +74,7 @@ public class HalfSingleTests PixelTypeInfo info = HalfSingle.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index 710135ed6..04e3ee35f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -96,6 +96,7 @@ public class HalfVector2Tests PixelTypeInfo info = HalfVector2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index 3e3f6a2ca..e8420ddd0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -87,6 +87,7 @@ public class HalfVector4Tests PixelTypeInfo info = HalfVector4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 3a8239923..337d02084 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -166,6 +166,7 @@ public class L16Tests PixelTypeInfo info = L16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Grayscale, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index 631a0e234..81f4d61fb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -261,6 +261,7 @@ public class L8Tests PixelTypeInfo info = L8.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Grayscale, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index 9fa0e3263..612d0f81a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -265,6 +265,7 @@ public class La16Tests PixelTypeInfo info = La16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Grayscale | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index 089c2076b..f55707c05 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -172,6 +172,7 @@ public class La32Tests PixelTypeInfo info = La32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Grayscale | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 9f24346a0..89c0ae69f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -88,6 +88,7 @@ public class NormalizedByte2Tests PixelTypeInfo info = NormalizedByte2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 63bb9d5f5..297438c65 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -238,6 +238,7 @@ public class NormalizedByte4Tests PixelTypeInfo info = NormalizedByte4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index f262ce177..4b912d866 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -92,6 +92,7 @@ public class NormalizedShort2Tests PixelTypeInfo info = NormalizedShort2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index 05058ae4d..0d9765888 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -239,6 +239,7 @@ public class NormalizedShort4Tests PixelTypeInfo info = NormalizedShort4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index 9ab1ec963..fac767812 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -90,6 +90,7 @@ public class Rg32Tests PixelTypeInfo info = Rg32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index ab4269d00..385126a65 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -137,6 +137,7 @@ public class Rgb24Tests PixelTypeInfo info = Rgb24.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(3, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index faf9bdb3c..625a9187f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -82,6 +82,7 @@ public class Rgb48Tests PixelTypeInfo info = Rgb48.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(3, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 7cf8ced45..a8243064d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -253,6 +253,7 @@ public class Rgba1010102Tests PixelTypeInfo info = Rgba1010102.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 9b96aeb7b..35fab151c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -313,6 +313,7 @@ public class Rgba32Tests PixelTypeInfo info = Rgba32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 49336400c..4f335b1e8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -315,6 +315,7 @@ public class Rgba64Tests PixelTypeInfo info = Rgba64.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index 28ee5ace5..b263bf8ea 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -211,6 +211,7 @@ public class RgbaVectorTests PixelTypeInfo info = RgbaVector.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index d709b7605..1143925cb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -167,6 +167,7 @@ public class Short2Tests PixelTypeInfo info = Short2.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index 2b5760342..ab3cc3b6e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -220,6 +220,7 @@ public class Short4Tests PixelTypeInfo info = Short4.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(4, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 495f5f9f6..983cf4d46 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -263,7 +263,11 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public struct TestPixelForAgnosticDecode : IPixel { - public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create(PixelComponentInfo.Create(2, 8, 8), PixelAlphaRepresentation.None); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); public PixelOperations CreatePixelOperations() => new(); From 057edd8ddd010f3f83446568c54c6565a9c4a171 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 5 Jan 2024 20:05:06 +1000 Subject: [PATCH 038/220] Use the new metadata --- src/ImageSharp/Color/Color.cs | 4 +- src/ImageSharp/Formats/Png/PngBitDepth.cs | 2 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 104 ++++++++++-------- .../PixelFormats/PixelComponentBitDepth.cs | 50 +++++++++ .../PixelFormats/PixelComponentPrecision.cs | 70 ------------ src/ImageSharp/PixelFormats/PixelTypeInfo.cs | 3 +- .../Formats/Png/PngEncoderTests.cs | 20 ++-- 7 files changed, 125 insertions(+), 128 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelComponentPrecision.cs diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index ec19a86eb..e61abf86f 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -94,7 +94,7 @@ public readonly partial struct Color : IEquatable { // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { return new(pixel.ToScaledVector4()); } @@ -118,7 +118,7 @@ public readonly partial struct Color : IEquatable // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentPrecision.Float) + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { for (int i = 0; i < destination.Length; i++) { diff --git a/src/ImageSharp/Formats/Png/PngBitDepth.cs b/src/ImageSharp/Formats/Png/PngBitDepth.cs index 452839d1d..a5cd2026b 100644 --- a/src/ImageSharp/Formats/Png/PngBitDepth.cs +++ b/src/ImageSharp/Formats/Png/PngBitDepth.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. // Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it. diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 8bf8be2ad..aa3603cfb 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1466,23 +1466,48 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Use options, then check metadata, if nothing set there then we suggest // a sensible default based upon the pixel format. - this.colorType = encoder.ColorType ?? pngMetadata.ColorType ?? SuggestColorType(); - if (!encoder.FilterMethod.HasValue) + PngColorType? colorType = encoder.ColorType ?? pngMetadata.ColorType; + byte? bits = (byte?)(encoder.BitDepth ?? pngMetadata.BitDepth); + + if (colorType is null || bits is null) { - // Specification recommends default filter method None for paletted images and Paeth for others. - this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth; + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + PixelComponentInfo? componentInfo = info.ComponentInfo; + + colorType ??= SuggestColorType(in info); + + if (bits is null) + { + // TODO: Update once we stop abusing PixelTypeInfo in decoders. + if (componentInfo.HasValue) + { + PixelComponentInfo c = componentInfo.Value; + bits = (byte)SuggestBitDepth(in c); + } + else + { + bits = (byte)PngBitDepth.Bit8; + } + } } // Ensure bit depth and color type are a supported combination. // Bit8 is the only bit depth supported by all color types. - byte bits = (byte)(encoder.BitDepth ?? pngMetadata.BitDepth ?? SuggestBitDepth()); - byte[] validBitDepths = PngConstants.ColorTypes[this.colorType]; + byte[] validBitDepths = PngConstants.ColorTypes[colorType.Value]; if (Array.IndexOf(validBitDepths, bits) == -1) { bits = (byte)PngBitDepth.Bit8; } - this.bitDepth = bits; + this.colorType = colorType.Value; + this.bitDepth = bits.Value; + + if (!encoder.FilterMethod.HasValue) + { + // Specification recommends default filter method None for paletted images and Paeth for others. + this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth; + } + use16Bit = bits == (byte)PngBitDepth.Bit16; bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit); @@ -1611,53 +1636,44 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// /// Returns a suggested for the given - /// This is not exhaustive but covers many common pixel formats. /// + /// The pixel type info. /// The type of pixel format. - private static PngColorType SuggestColorType() + private static PngColorType SuggestColorType(in PixelTypeInfo info) where TPixel : unmanaged, IPixel - => default(TPixel) switch - { - A8 => PngColorType.GrayscaleWithAlpha, - Argb32 => PngColorType.RgbWithAlpha, - Bgr24 => PngColorType.Rgb, - Bgra32 => PngColorType.RgbWithAlpha, - L8 => PngColorType.Grayscale, - L16 => PngColorType.Grayscale, - La16 => PngColorType.GrayscaleWithAlpha, - La32 => PngColorType.GrayscaleWithAlpha, - Rgb24 => PngColorType.Rgb, - Rgba32 => PngColorType.RgbWithAlpha, - Rgb48 => PngColorType.Rgb, - Rgba64 => PngColorType.RgbWithAlpha, - RgbaVector => PngColorType.RgbWithAlpha, - _ => PngColorType.RgbWithAlpha + { + if (info.AlphaRepresentation == PixelAlphaRepresentation.None) + { + return info.ColorType switch + { + PixelColorType.Grayscale => PngColorType.Grayscale, + _ => PngColorType.Rgb, + }; + } + + return info.ColorType switch + { + PixelColorType.Grayscale | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha, + _ => PngColorType.RgbWithAlpha, }; + } /// /// Returns a suggested for the given - /// This is not exhaustive but covers many common pixel formats. /// + /// The pixel type info. /// The type of pixel format. - private static PngBitDepth SuggestBitDepth() + private static PngBitDepth SuggestBitDepth(in PixelComponentInfo info) where TPixel : unmanaged, IPixel - => default(TPixel) switch - { - A8 => PngBitDepth.Bit8, - Argb32 => PngBitDepth.Bit8, - Bgr24 => PngBitDepth.Bit8, - Bgra32 => PngBitDepth.Bit8, - L8 => PngBitDepth.Bit8, - L16 => PngBitDepth.Bit16, - La16 => PngBitDepth.Bit8, - La32 => PngBitDepth.Bit16, - Rgb24 => PngBitDepth.Bit8, - Rgba32 => PngBitDepth.Bit8, - Rgb48 => PngBitDepth.Bit16, - Rgba64 => PngBitDepth.Bit16, - RgbaVector => PngBitDepth.Bit16, - _ => PngBitDepth.Bit8 - }; + { + int bits = info.GetMaximumComponentPrecision(); + if (bits > (int)PixelComponentBitDepth.Bit8) + { + return PngBitDepth.Bit16; + } + + return PngBitDepth.Bit8; + } private unsafe struct ScratchBuffer { diff --git a/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs b/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs new file mode 100644 index 000000000..674c9363b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides enumeration of the precision in bits of individual components within a pixel format. +/// +public enum PixelComponentBitDepth +{ + /// + /// 1 bit per component. + /// + Bit1 = 1, + + /// + /// 2 bits per component. + /// + Bit2 = 2, + + /// + /// 4 bits per component. + /// + Bit4 = 4, + + /// + /// 8 bits per component. + /// + Bit8 = 8, + + /// + /// 16 bits per component. + /// + Bit16 = 16, + + /// + /// 32 bits per component. + /// + Bit32 = 32, + + /// + /// 64 bits per component. + /// + Bit64 = 64, + + /// + /// 128 bits per component. + /// + Bit128 = 128 +} diff --git a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs b/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs deleted file mode 100644 index 2f15b7fad..000000000 --- a/src/ImageSharp/PixelFormats/PixelComponentPrecision.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.PixelFormats; - -/// -/// Provides enumeration of the precision in bits of individual components within a pixel format. -/// -public enum PixelComponentPrecision -{ - /// - /// 8-bit signed integer. - /// - SByte = sizeof(sbyte) * 8, - - /// - /// 8-bit unsigned integer. - /// - Byte = sizeof(byte) * 8, - - /// - /// 16-bit signed integer. - /// - Short = sizeof(short) * 8, - - /// - /// 16-bit unsigned integer. - /// - UShort = sizeof(ushort) * 8, - - /// - /// 32-bit signed integer. - /// - Int = sizeof(int) * 8, - - /// - /// 32-bit unsigned integer. - /// - UInt = sizeof(uint) * 8, - - /// - /// 64-bit signed integer. - /// - Long = sizeof(long) * 8, - - /// - /// 64-bit unsigned integer. - /// - ULong = sizeof(ulong) * 8, - - /// - /// 16-bit floating point. - /// - Half = (sizeof(float) * 8) / 2, - - /// - /// 32-bit floating point. - /// - Float = sizeof(float) * 8, - - /// - /// 64-bit floating point. - /// - Double = sizeof(double) * 8, - - /// - /// 128-bit floating point. - /// - Decimal = sizeof(decimal) * 8, -} diff --git a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs index 81c70a18f..7cd1284f4 100644 --- a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -4,8 +4,9 @@ using System.Runtime.CompilerServices; // TODO: Review this type as it's used to represent 2 different things. -// 1.The encoded image pixel format. +// 1. The encoded image pixel format. // 2. The pixel format of the decoded image. +// Only the bits per pixel is used by the decoder, we should make it a property of the image metadata. namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 477d88e9a..825becb36 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -256,21 +256,21 @@ public partial class PngEncoderTests [Theory] [WithBlankImages(1, 1, PixelTypes.A8, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Argb32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgra4444, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Byte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.NormalizedByte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] [WithBlankImages(1, 1, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.RgbaVector, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] [WithBlankImages(1, 1, PixelTypes.Rgb24, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgr24, PngColorType.Rgb, PngBitDepth.Bit8)] [WithBlankImages(1, 1, PixelTypes.Bgra32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] From 029381e2d3b15743b615db80d5fdbf4f5275a259 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 5 Jan 2024 20:26:32 +1000 Subject: [PATCH 039/220] Remove outdated rule overrides. --- shared-infrastructure | 2 +- src/ImageSharp.ruleset | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/shared-infrastructure b/shared-infrastructure index 1c526a97e..d65232bbb 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 1c526a97eea8bcbc7c79de095676f7fb975a9fb1 +Subproject commit d65232bbbfe55a9a153b4058139dda5230e6eb4f diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index 6c291bfb1..f29278c95 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -2,7 +2,5 @@ - - From bb9ed6533370ed83ee7adc7d507d5b1f3929fa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Sun, 7 Jan 2024 15:49:34 +0100 Subject: [PATCH 040/220] add jpeg com marker support --- ImageSharp.sln | 1 + .../Formats/Jpeg/JpegDecoderCore.cs | 20 ++++++++- .../Formats/Jpeg/JpegEncoderCore.cs | 45 +++++++++++++++++++ src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 7 +++ .../Formats/Jpeg/MetadataExtensions.cs | 21 +++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 15 +++++++ .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 44 ++++++++++++++++++ .../Formats/Jpg/JpegMetadataTests.cs | 24 +++++++++- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Jpg/issues/issue-2067-comment.jpg | 3 ++ 10 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/issue-2067-comment.jpg diff --git a/ImageSharp.sln b/ImageSharp.sln index 2967acb8f..82eeefcde 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -237,6 +237,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68 tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg = tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg = tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg = tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg + tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6AC2-417B-AD79-9BD5D0D378A0}" diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index ccace190f..e11923ac8 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -481,7 +482,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals case JpegConstants.Markers.APP15: case JpegConstants.Markers.COM: - stream.Skip(markerContentByteSize); + this.ProcessComMarker(stream, markerContentByteSize); break; case JpegConstants.Markers.DAC: @@ -515,6 +516,23 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.scanDecoder = null; } + /// + /// Assigns COM marker bytes to comment property + /// + /// The input stream. + /// The remaining bytes in the segment block. + private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize) + { + Span temp = stackalloc byte[markerContentByteSize]; + char[] chars = new char[markerContentByteSize]; + JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); + + stream.Read(temp); + Encoding.ASCII.GetChars(temp, chars); + + metadata.Comments.Add(chars); + } + /// /// Returns encoded colorspace based on the adobe APP14 marker. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 7fc2a1f45..cc6042b6e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -3,6 +3,7 @@ #nullable disable using System.Buffers.Binary; +using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; @@ -89,6 +90,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals // Write Exif, XMP, ICC and IPTC profiles this.WriteProfiles(metadata, buffer); + // Write comments + this.WriteComment(jpegMetadata); + // Write the image dimensions. this.WriteStartOfFrame(image.Width, image.Height, frameConfig, buffer); @@ -167,6 +171,47 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals this.outputStream.Write(buffer, 0, 18); } + /// + /// Writes comment + /// + /// The image metadata. + private void WriteComment(JpegMetadata metadata) + { + if (metadata.Comments is { Count: 0 }) + { + return; + } + + // Length (comment strings lengths) + (comments markers with payload sizes) + int commentsBytes = metadata.Comments.Sum(x => x.Length) + (metadata.Comments.Count * 4); + int commentStart = 0; + Span commentBuffer = stackalloc byte[commentsBytes]; + + foreach (Memory comment in metadata.Comments) + { + int totalComLength = comment.Length + 4; + + Span commentData = commentBuffer.Slice(commentStart, totalComLength); + Span markers = commentData.Slice(0, 2); + Span payloadSize = commentData.Slice(2, 2); + Span payload = commentData.Slice(4, comment.Length); + + // Beginning of comment ff fe + markers[0] = JpegConstants.Markers.XFF; + markers[1] = JpegConstants.Markers.COM; + + // Write payload size + BinaryPrimitives.WriteInt16BigEndian(payloadSize, (short)(commentData.Length - 2)); + + Encoding.ASCII.GetBytes(comment.Span, payload); + + // Indicate begin of next comment in buffer + commentStart += totalComLength; + } + + this.outputStream.Write(commentBuffer, 0, commentBuffer.Length); + } + /// /// Writes the Define Huffman Table marker and tables. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 59fc2f9cb..61fe3b214 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -15,6 +15,7 @@ public class JpegMetadata : IDeepCloneable /// public JpegMetadata() { + this.Comments = new List>(); } /// @@ -25,6 +26,7 @@ public class JpegMetadata : IDeepCloneable { this.ColorType = other.ColorType; + this.Comments = other.Comments; this.LuminanceQuality = other.LuminanceQuality; this.ChrominanceQuality = other.ChrominanceQuality; } @@ -101,6 +103,11 @@ public class JpegMetadata : IDeepCloneable /// public bool? Progressive { get; internal set; } + /// + /// Gets the comments. + /// + public ICollection>? Comments { get; } + /// public IDeepCloneable DeepClone() => new JpegMetadata(this); } diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs index 753dfdb60..53efc7d0c 100644 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Text; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Metadata; @@ -17,4 +18,24 @@ public static partial class MetadataExtensions /// The metadata this method extends. /// The . public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); + + /// + /// Saves the comment into + /// + /// The metadata this method extends. + /// The comment string. + public static void SaveComment(this JpegMetadata metadata, string comment) + { + ASCIIEncoding encoding = new(); + + byte[] bytes = encoding.GetBytes(comment); + metadata.Comments?.Add(encoding.GetChars(bytes)); + } + + /// + /// Gets the comments from + /// + /// The metadata this method extends. + /// The IEnumerable string of comments. + public static IEnumerable? GetComments(this JpegMetadata metadata) => metadata.Comments?.Select(x => x.ToString()); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index c8d93f6e9..b219e715f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -364,4 +364,19 @@ public partial class JpegDecoderTests image.DebugSave(provider); image.CompareToOriginal(provider); } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2067_CommentMarker, PixelTypes.Rgba32)] + public void JpegDecoder_DecodeMetadataComment(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + string expectedComment = "TEST COMMENT"; + using Image image = provider.GetImage(JpegDecoder.Instance); + JpegMetadata metadata = image.Metadata.GetJpegMetadata(); + + Assert.Equal(1, metadata.Comments?.Count); + Assert.Equal(expectedComment, metadata.GetComments()?.FirstOrDefault()); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 2b721b9b5..50f47a134 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -154,6 +154,50 @@ public partial class JpegEncoderTests } } + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2067_CommentMarker, PixelTypes.Rgba32)] + public void Encode_PreservesComments(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // arrange + using var input = provider.GetImage(JpegDecoder.Instance); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, JpegEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + JpegMetadata actual = output.Metadata.GetJpegMetadata(); + Assert.NotEmpty(actual.Comments); + Assert.Equal(1, actual.Comments.Count); + Assert.Equal("TEST COMMENT", actual.Comments.ElementAt(0).ToString()); + } + + [Fact] + public void Encode_SavesMultipleComments() + { + // arrange + using var input = new Image(1, 1); + JpegMetadata meta = input.Metadata.GetJpegMetadata(); + using var memStream = new MemoryStream(); + + // act + meta.SaveComment("First comment"); + meta.SaveComment("Second Comment"); + input.Save(memStream, JpegEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + JpegMetadata actual = output.Metadata.GetJpegMetadata(); + Assert.NotEmpty(actual.Comments); + Assert.Equal(2, actual.Comments.Count); + Assert.Equal(meta.Comments.ElementAt(0).ToString(), actual.Comments.ElementAt(0).ToString()); + Assert.Equal(meta.Comments.ElementAt(1).ToString(), actual.Comments.ElementAt(1).ToString()); + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegEncodingColor.Luminance)] [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 05f22667d..901bb4619 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Collections.ObjectModel; using SixLabors.ImageSharp.Formats.Jpeg; namespace SixLabors.ImageSharp.Tests.Formats.Jpg; @@ -55,6 +56,27 @@ public class JpegMetadataTests var meta = new JpegMetadata { LuminanceQuality = qualityLuma, ChrominanceQuality = qualityChroma }; - Assert.Equal(meta.Quality, qualityLuma); + Assert.Equal(meta.Quality, qualityLuma); + } + + [Fact] + public void Comment_EmptyComment() + { + var meta = new JpegMetadata(); + + Assert.True(Array.Empty>().SequenceEqual(meta.Comments)); + } + + [Fact] + public void Comment_OnlyComment() + { + string comment = "test comment"; + var expectedCollection = new Collection> { new(comment.ToCharArray()) }; + + var meta = new JpegMetadata(); + meta.Comments?.Add(comment.ToCharArray()); + + Assert.Equal(1, meta.Comments?.Count); + Assert.True(expectedCollection.FirstOrDefault().ToString() == meta.Comments?.FirstOrDefault().ToString()); } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8aa95d349..0dab4ff87 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -309,6 +309,7 @@ public static class TestImages public const string Issue2564 = "Jpg/issues/issue-2564.jpg"; public const string HangBadScan = "Jpg/issues/Hang_C438A851.jpg"; public const string Issue2517 = "Jpg/issues/issue2517-bad-d7.jpg"; + public const string Issue2067_CommentMarker = "Jpg/issues/issue-2067-comment.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue-2067-comment.jpg b/tests/Images/Input/Jpg/issues/issue-2067-comment.jpg new file mode 100644 index 000000000..18dc6f2e3 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2067-comment.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d87b5429adeffcfac535aa8af2ec9801bf6c965a2e6751cfec4f8534195ba8f4 +size 21082 From b3a8452edc95ed4603b13fc78dab90394b5a2891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Sun, 7 Jan 2024 18:53:03 +0100 Subject: [PATCH 041/220] Rename SaveComment to SetComment, add index, add clearcomments --- .../Formats/Jpeg/MetadataExtensions.cs | 23 +++++++++++++++---- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 15 ++++++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 15 ------------ .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 10 ++++---- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs index 53efc7d0c..0c66fcbdd 100644 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs @@ -20,22 +20,35 @@ public static partial class MetadataExtensions public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); /// - /// Saves the comment into + /// Sets the comment in /// /// The metadata this method extends. + /// The index of comment to be inserted to. /// The comment string. - public static void SaveComment(this JpegMetadata metadata, string comment) + public static void SetComment(this JpegMetadata metadata, int index, string comment) { - ASCIIEncoding encoding = new(); + if (metadata.Comments == null) + { + return; + } + ASCIIEncoding encoding = new(); byte[] bytes = encoding.GetBytes(comment); - metadata.Comments?.Add(encoding.GetChars(bytes)); + List>? comments = metadata.Comments as List>; + comments?.Insert(index, encoding.GetChars(bytes)); } /// /// Gets the comments from /// /// The metadata this method extends. + /// The index of comment. /// The IEnumerable string of comments. - public static IEnumerable? GetComments(this JpegMetadata metadata) => metadata.Comments?.Select(x => x.ToString()); + public static string? GetComment(this JpegMetadata metadata, int index) => metadata.Comments?.ElementAtOrDefault(index).ToString(); + + /// + /// Clears comments + /// + /// The . + public static void ClearComments(this JpegMetadata metadata) => metadata.Comments?.Clear(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 1c203e734..fb37a956d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -425,6 +425,21 @@ public partial class JpegDecoderTests VerifyEncodedStrings(exif); } + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2067_CommentMarker, PixelTypes.Rgba32)] + public void JpegDecoder_DecodeMetadataComment(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + string expectedComment = "TEST COMMENT"; + using Image image = provider.GetImage(JpegDecoder.Instance); + JpegMetadata metadata = image.Metadata.GetJpegMetadata(); + + Assert.Equal(1, metadata.Comments?.Count); + Assert.Equal(expectedComment, metadata.GetComment(0)); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + private static void VerifyEncodedStrings(ExifProfile exif) { Assert.NotNull(exif); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index b219e715f..c8d93f6e9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -364,19 +364,4 @@ public partial class JpegDecoderTests image.DebugSave(provider); image.CompareToOriginal(provider); } - - [Theory] - [WithFile(TestImages.Jpeg.Issues.Issue2067_CommentMarker, PixelTypes.Rgba32)] - public void JpegDecoder_DecodeMetadataComment(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - string expectedComment = "TEST COMMENT"; - using Image image = provider.GetImage(JpegDecoder.Instance); - JpegMetadata metadata = image.Metadata.GetJpegMetadata(); - - Assert.Equal(1, metadata.Comments?.Count); - Assert.Equal(expectedComment, metadata.GetComments()?.FirstOrDefault()); - image.DebugSave(provider); - image.CompareToOriginal(provider); - } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 50f47a134..fa54859a3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -184,8 +184,8 @@ public partial class JpegEncoderTests using var memStream = new MemoryStream(); // act - meta.SaveComment("First comment"); - meta.SaveComment("Second Comment"); + meta.SetComment(0, "First comment"); + meta.SetComment(1, "Second Comment"); input.Save(memStream, JpegEncoder); // assert @@ -193,9 +193,9 @@ public partial class JpegEncoderTests using var output = Image.Load(memStream); JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); - Assert.Equal(2, actual.Comments.Count); - Assert.Equal(meta.Comments.ElementAt(0).ToString(), actual.Comments.ElementAt(0).ToString()); - Assert.Equal(meta.Comments.ElementAt(1).ToString(), actual.Comments.ElementAt(1).ToString()); + Assert.Equal(2, actual.Comments?.Count); + Assert.Equal(meta.Comments?.ElementAt(0).ToString(), actual.Comments?.ElementAt(0).ToString()); + Assert.Equal(meta.Comments?.ElementAt(1).ToString(), actual.Comments?.ElementAt(1).ToString()); } [Theory] From d6165909fd7be5e45b2f176373dd9694e1ab93e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Sun, 7 Jan 2024 20:13:19 +0100 Subject: [PATCH 042/220] tab --- tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 901bb4619..8b991228b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -56,7 +56,7 @@ public class JpegMetadataTests var meta = new JpegMetadata { LuminanceQuality = qualityLuma, ChrominanceQuality = qualityChroma }; - Assert.Equal(meta.Quality, qualityLuma); + Assert.Equal(meta.Quality, qualityLuma); } [Fact] From 84a7988f5d60434e90c496493fe060ea91b81911 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 8 Jan 2024 18:43:06 +0100 Subject: [PATCH 043/220] TrueColor images with 32 bits per pixel are assumed to always have 8 bit alpha channel --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 8 +++++++- src/ImageSharp/Formats/Tga/TgaFileHeader.cs | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 26e057bff..eb0e508cd 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -937,7 +937,9 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; - int alphaBits = this.fileHeader.ImageDescriptor & 0xf; + // TrueColor images with 32 bits per pixel are assumed to always have 8 bit alpha channel, + // because some encoders do not set correctly the alpha bits in the image descriptor. + int alphaBits = this.IsTrueColor32BitPerPixel(this.tgaMetadata.BitsPerPixel) ? 8 : this.fileHeader.ImageDescriptor & 0xf; if (alphaBits is not 0 and not 1 and not 8) { TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits"); @@ -949,4 +951,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals // Bits 4 and 5 describe the image origin. return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); } + + private bool IsTrueColor32BitPerPixel(TgaBitsPerPixel bitsPerPixel) => bitsPerPixel == TgaBitsPerPixel.Pixel32 && + (this.fileHeader.ImageType == TgaImageType.TrueColor || + this.fileHeader.ImageType == TgaImageType.RleTrueColor); } diff --git a/src/ImageSharp/Formats/Tga/TgaFileHeader.cs b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs index 007dc03de..2613cd610 100644 --- a/src/ImageSharp/Formats/Tga/TgaFileHeader.cs +++ b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs @@ -131,10 +131,7 @@ internal readonly struct TgaFileHeader /// public byte ImageDescriptor { get; } - public static TgaFileHeader Parse(Span data) - { - return MemoryMarshal.Cast(data)[0]; - } + public static TgaFileHeader Parse(Span data) => MemoryMarshal.Cast(data)[0]; public void WriteTo(Span buffer) { From 1c2bfd8ebfbbb76793678732ea6ad094889f3d80 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 11 Jan 2024 19:21:54 +0100 Subject: [PATCH 044/220] Add test for issue 2629 --- .../Formats/Tga/TgaDecoderTests.cs | 16 ++++++++++++++-- tests/ImageSharp.Tests/TestImages.cs | 2 ++ tests/Images/Input/Tga/issues/Issue2629.tga | 3 +++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Tga/issues/Issue2629.tga diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index da5de8e89..9efbac6a3 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; @@ -724,7 +723,7 @@ public class TgaDecoderTests { using (Image image = provider.GetImage(TgaDecoder.Instance)) { - // Using here the reference output instead of the the reference decoder, + // Using here the reference output instead of the reference decoder, // because the reference decoder does not ignore the alpha data here. image.DebugSave(provider); image.CompareToReferenceOutput(ImageComparer.Exact, provider); @@ -771,6 +770,19 @@ public class TgaDecoderTests appendPixelTypeToFileName: false); } + // https://github.com/SixLabors/ImageSharp/issues/2629 + [Theory] + [WithFile(Issue2629, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Issue2629(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder.Instance)) + { + image.DebugSave(provider); + ImageComparingUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8aa95d349..65ef95e9d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -617,6 +617,8 @@ public static class TestImages public const string Github_RLE_legacy = "Tga/Github_RLE_legacy.tga"; public const string WhiteStripesPattern = "Tga/whitestripes.png"; + + public const string Issue2629 = "Tga/issues/Issue2629.tga"; } public static class Webp diff --git a/tests/Images/Input/Tga/issues/Issue2629.tga b/tests/Images/Input/Tga/issues/Issue2629.tga new file mode 100644 index 000000000..4c87196ad --- /dev/null +++ b/tests/Images/Input/Tga/issues/Issue2629.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:defc1396481f426a74e8af51ed57f65cbed932f932673ce5a87fa12ea9b460f8 +size 32786 From 3969525d2ae9ee89055ab220528d1423da156b60 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 13 Jan 2024 10:13:44 +1000 Subject: [PATCH 045/220] Port shuffle4 --- src/ImageSharp/Common/Helpers/Numerics.cs | 7 +- .../Helpers/Shuffle/IComponentShuffle.cs | 77 +++-- .../Common/Helpers/Shuffle/IPad3Shuffle4.cs | 8 +- .../Common/Helpers/Shuffle/IShuffle3.cs | 4 +- .../Common/Helpers/Shuffle/IShuffle4Slice3.cs | 8 +- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 264 +++++++++++------- .../Common/Helpers/SimdUtils.Shuffle.cs | 125 +++++---- .../Formats/Webp/BitWriter/BitWriterBase.cs | 1 - .../Webp/Chunks/WebpAnimationParameter.cs | 1 - .../Formats/Webp/Chunks/WebpFrameData.cs | 2 - .../Formats/Webp/Chunks/WebpVp8X.cs | 2 - .../Formats/Webp/Lossless/Vp8LEncoder.cs | 1 - .../Formats/Webp/Lossy/Vp8Encoder.cs | 1 - .../Helpers => Formats/Webp}/RiffHelper.cs | 2 +- .../Utils/Vector4Converters.RgbaCompatible.cs | 18 +- 15 files changed, 302 insertions(+), 219 deletions(-) rename src/ImageSharp/{Common/Helpers => Formats/Webp}/RiffHelper.cs (98%) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 293997c4d..ca28a7aab 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -5,7 +5,6 @@ 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; @@ -61,6 +60,12 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static nint Modulo4(nint x) => x & 3; + /// + /// Calculates % 4 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nuint Modulo4(nuint x) => x & 3; + /// /// Calculates % 8 /// diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs index 683ac518b..d4ab8c618 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs @@ -19,24 +19,26 @@ namespace SixLabors.ImageSharp; internal interface IComponentShuffle { /// - /// Shuffles then slices 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Shuffles then slices 8-bit integers in + /// using the control and store the results in . + /// If successful, this method will reduce the length of length + /// by the shuffle amount. /// /// The source span of bytes. - /// The destination span of bytes. - void ShuffleReduce(ref ReadOnlySpan source, ref Span dest); + /// The destination span of bytes. + void ShuffleReduce(ref ReadOnlySpan source, ref Span destination); /// - /// Shuffle 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Shuffle 8-bit integers in + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// - /// Implementation can assume that source.Length is less or equal than dest.Length. + /// Implementation can assume that source.Length is less or equal than destination.Length. /// Loops should iterate using source.Length. /// - void RunFallbackShuffle(ReadOnlySpan source, Span dest); + void Shuffle(ReadOnlySpan source, Span destination); } /// @@ -44,24 +46,21 @@ internal interface IShuffle4 : IComponentShuffle { } -internal readonly struct DefaultShuffle4 : IShuffle4 +internal readonly struct DefaultShuffle4(byte control) : IShuffle4 { - public DefaultShuffle4(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); - Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); for (nuint i = 0; i < (uint)source.Length; i += 4) { @@ -76,14 +75,14 @@ internal readonly struct DefaultShuffle4 : IShuffle4 internal readonly struct WXYZShuffle4 : IShuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle2103); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); uint n = (uint)source.Length / 4; for (nuint i = 0; i < n; i++) @@ -100,14 +99,14 @@ internal readonly struct WXYZShuffle4 : IShuffle4 internal readonly struct WZYXShuffle4 : IShuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0123); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); uint n = (uint)source.Length / 4; for (nuint i = 0; i < n; i++) @@ -124,14 +123,14 @@ internal readonly struct WZYXShuffle4 : IShuffle4 internal readonly struct YZWXShuffle4 : IShuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0321); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); uint n = (uint)source.Length / 4; for (nuint i = 0; i < n; i++) @@ -148,14 +147,14 @@ internal readonly struct YZWXShuffle4 : IShuffle4 internal readonly struct ZYXWShuffle4 : IShuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3012); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); uint n = (uint)source.Length / 4; for (nuint i = 0; i < n; i++) @@ -179,14 +178,14 @@ internal readonly struct ZYXWShuffle4 : IShuffle4 internal readonly struct XWZYShuffle4 : IShuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle1230); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); uint n = (uint)source.Length / 4; for (nuint i = 0; i < n; i++) diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs index 6cf6eef08..255448d61 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs @@ -24,12 +24,12 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); Span temp = stackalloc byte[4]; ref byte t = ref MemoryMarshal.GetReference(temp); @@ -52,10 +52,10 @@ internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 { [MethodImpl(InliningOptions.ShortMethod)] public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3210); + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs index 2cd586212..89faca243 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs @@ -24,12 +24,12 @@ internal readonly struct DefaultShuffle3 : IShuffle3 => HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); for (nuint i = 0; i < (uint)source.Length; i += 3) { diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs index 5e82973e3..30fda7a8e 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs @@ -24,12 +24,12 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4) { @@ -44,10 +44,10 @@ internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 { [MethodImpl(InliningOptions.ShortMethod)] public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, Shuffle.MMShuffle3210); + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index ad079b52e..4732effd4 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -51,22 +51,32 @@ internal static partial class SimdUtils /// /// Shuffle single-precision (32-bit) floating-point elements in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The source span of floats. - /// The destination span of floats. + /// The destination span of floats. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4Reduce( ref ReadOnlySpan source, - ref Span dest, + ref Span destination, [ConstantExpected] byte control) { - if (Avx.IsSupported || Sse.IsSupported) + if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) { - int remainder = Avx.IsSupported - ? Numerics.ModuloP2(source.Length, Vector256.Count) - : Numerics.ModuloP2(source.Length, Vector128.Count); + int remainder = 0; + if (Vector512.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector512.Count); + } + else if (Vector256.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector256.Count); + } + else if (Vector128.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector128.Count); + } int adjustedCount = source.Length - remainder; @@ -74,17 +84,17 @@ internal static partial class SimdUtils { Shuffle4( source[..adjustedCount], - dest[..adjustedCount], + destination[..adjustedCount], control); source = source[adjustedCount..]; - dest = dest[adjustedCount..]; + destination = destination[adjustedCount..]; } } } /// - /// Shuffle 8-bit integers within 128-bit lanes in + /// Shuffle 8-bit integers /// using the control and store the results in . /// /// The source span of bytes. @@ -96,11 +106,21 @@ internal static partial class SimdUtils ref Span dest, byte control) { - if (Avx2.IsSupported || Ssse3.IsSupported) + if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) { - int remainder = Avx2.IsSupported - ? Numerics.ModuloP2(source.Length, Vector256.Count) - : Numerics.ModuloP2(source.Length, Vector128.Count); + int remainder = 0; + if (Vector512.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector512.Count); + } + else if (Vector256.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector256.Count); + } + else if (Vector128.IsHardwareAccelerated) + { + remainder = Numerics.ModuloP2(source.Length, Vector128.Count); + } int adjustedCount = source.Length - remainder; @@ -218,76 +238,102 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, [ConstantExpected] byte control) { - if (Avx.IsSupported) + if (Vector512.IsHardwareAccelerated) { - ref Vector256 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Span temp = stackalloc int[Vector512.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector512 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector256 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - nint n = (nint)dest.Vector256Count(); - nint m = Numerics.Modulo4(n); - nint u = n - m; + nuint n = (uint)destination.Length / (uint)Vector512.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; - for (nint i = 0; i < u; i += 4) + for (nuint i = 0; i < u; i += 4) { - ref Vector256 vd0 = ref Unsafe.Add(ref destBase, i); - ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Avx.Permute(vs0, control); - Unsafe.Add(ref vd0, 1) = Avx.Permute(Unsafe.Add(ref vs0, 1), control); - Unsafe.Add(ref vd0, 2) = Avx.Permute(Unsafe.Add(ref vs0, 2), control); - Unsafe.Add(ref vd0, 3) = Avx.Permute(Unsafe.Add(ref vs0, 3), control); + vd0 = Vector512.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { - for (nint i = u; i < n; i++) + for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destBase, i) = Avx.Permute(Unsafe.Add(ref sourceBase, i), control); + Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else + else if (Vector256.IsHardwareAccelerated) { - // Sse - ref Vector128 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Span temp = stackalloc int[Vector256.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - nint n = (nint)((uint)dest.Length / (uint)Vector128.Count); - nint m = Numerics.Modulo4(n); - nint u = n - m; + nuint n = (uint)destination.Length / (uint)Vector256.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; - for (nint i = 0; i < u; i += 4) + for (nuint i = 0; i < u; i += 4) { - ref Vector128 vd0 = ref Unsafe.Add(ref destBase, i); - ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Sse.Shuffle(vs0, vs0, control); + vd0 = Vector256.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + } - Vector128 vs1 = Unsafe.Add(ref vs0, 1); - Unsafe.Add(ref vd0, 1) = Sse.Shuffle(vs1, vs1, control); + if (m > 0) + { + for (nuint i = u; i < n; i++) + { + Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + } + } + } + else if (Vector128.IsHardwareAccelerated) + { + Span temp = stackalloc int[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); + + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - Vector128 vs2 = Unsafe.Add(ref vs0, 2); - Unsafe.Add(ref vd0, 2) = Sse.Shuffle(vs2, vs2, control); + nuint n = (uint)destination.Length / (uint)Vector128.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; + + for (nuint i = 0; i < u; i += 4) + { + ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - Vector128 vs3 = Unsafe.Add(ref vs0, 3); - Unsafe.Add(ref vd0, 3) = Sse.Shuffle(vs3, vs3, control); + vd0 = Vector128.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { - for (nint i = u; i < n; i++) + for (nuint i = u; i < n; i++) { - Vector128 vs = Unsafe.Add(ref sourceBase, i); - Unsafe.Add(ref destBase, i) = Sse.Shuffle(vs, vs, control); + Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } @@ -296,80 +342,102 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, byte control) { - if (Avx2.IsSupported) + if (Vector512.IsHardwareAccelerated) { - // I've chosen to do this for convenience while we determine what - // shuffle controls to add to the library. - // We can add static ROS instances if need be in the future. - Span bytes = stackalloc byte[Vector256.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector256 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Span temp = stackalloc byte[Vector512.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector512 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector256 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + nuint n = (uint)destination.Length / (uint)Vector512.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; + + for (nuint i = 0; i < u; i += 4) + { + ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); + ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); + + vd0 = Vector512.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + } + + if (m > 0) + { + for (nuint i = u; i < n; i++) + { + Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + } + } + } + else if (Vector256.IsHardwareAccelerated) + { + Span temp = stackalloc byte[Vector256.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - nint n = (nint)((uint)dest.Length / (uint)Vector256.Count); - nint m = Numerics.Modulo4(n); - nint u = n - m; + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - for (nint i = 0; i < u; i += 4) + nuint n = (uint)destination.Length / (uint)Vector256.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; + + for (nuint i = 0; i < u; i += 4) { ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); - ref Vector256 vd0 = ref Unsafe.Add(ref destBase, i); + ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Avx2.Shuffle(vs0, vshuffle); - Unsafe.Add(ref vd0, 1) = Avx2.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); - Unsafe.Add(ref vd0, 2) = Avx2.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); - Unsafe.Add(ref vd0, 3) = Avx2.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); + vd0 = Vector256.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { - for (nint i = u; i < n; i++) + for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destBase, i) = Avx2.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); + Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else + else if (Vector128.IsHardwareAccelerated) { - // Ssse3 - Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); - - ref Vector128 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Span temp = stackalloc byte[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - nint n = (nint)((uint)dest.Length / (uint)Vector128.Count); - nint m = Numerics.Modulo4(n); - nint u = n - m; + nuint n = (uint)destination.Length / (uint)Vector128.Count; + nuint m = Numerics.Modulo4(n); + nuint u = n - m; - for (nint i = 0; i < u; i += 4) + for (nuint i = 0; i < u; i += 4) { ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); - ref Vector128 vd0 = ref Unsafe.Add(ref destBase, i); + ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Ssse3.Shuffle(vs0, vshuffle); - Unsafe.Add(ref vd0, 1) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); - Unsafe.Add(ref vd0, 2) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); - Unsafe.Add(ref vd0, 3) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); + vd0 = Vector128.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { - for (nint i = u; i < n; i++) + for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destBase, i) = Ssse3.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); + Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs index 83cd3d246..dbeb54a80 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs @@ -12,140 +12,140 @@ internal static partial class SimdUtils { /// /// Shuffle single-precision (32-bit) floating-point elements in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The source span of floats. - /// The destination span of floats. + /// The destination span of floats. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, [ConstantExpected] byte control) { - VerifyShuffle4SpanInput(source, dest); + VerifyShuffle4SpanInput(source, destination); - HwIntrinsics.Shuffle4Reduce(ref source, ref dest, control); + HwIntrinsics.Shuffle4Reduce(ref source, ref destination, control); // Deal with the remainder: if (source.Length > 0) { - Shuffle4Remainder(source, dest, control); + Shuffle4Remainder(source, destination, control); } } /// /// Shuffle 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The type of shuffle struct. /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The type of shuffle to perform. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, TShuffle shuffle) where TShuffle : struct, IShuffle4 { - VerifyShuffle4SpanInput(source, dest); + VerifyShuffle4SpanInput(source, destination); - shuffle.ShuffleReduce(ref source, ref dest); + shuffle.ShuffleReduce(ref source, ref destination); // Deal with the remainder: if (source.Length > 0) { - shuffle.RunFallbackShuffle(source, dest); + shuffle.Shuffle(source, destination); } } /// /// Shuffle 8-bit integer triplets within 128-bit lanes in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The type of shuffle struct. /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The type of shuffle to perform. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle3( ReadOnlySpan source, - Span dest, + Span destination, TShuffle shuffle) where TShuffle : struct, IShuffle3 { - // Source length should be smaller than dest length, and divisible by 3. - VerifyShuffle3SpanInput(source, dest); + // Source length should be smaller than destination length, and divisible by 3. + VerifyShuffle3SpanInput(source, destination); - shuffle.ShuffleReduce(ref source, ref dest); + shuffle.ShuffleReduce(ref source, ref destination); // Deal with the remainder: if (source.Length > 0) { - shuffle.RunFallbackShuffle(source, dest); + shuffle.Shuffle(source, destination); } } /// /// Pads then shuffles 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The type of shuffle struct. /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The type of shuffle to perform. [MethodImpl(InliningOptions.ShortMethod)] public static void Pad3Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, TShuffle shuffle) where TShuffle : struct, IPad3Shuffle4 { - VerifyPad3Shuffle4SpanInput(source, dest); + VerifyPad3Shuffle4SpanInput(source, destination); - shuffle.ShuffleReduce(ref source, ref dest); + shuffle.ShuffleReduce(ref source, ref destination); // Deal with the remainder: if (source.Length > 0) { - shuffle.RunFallbackShuffle(source, dest); + shuffle.Shuffle(source, destination); } } /// /// Shuffles then slices 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The type of shuffle struct. /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The type of shuffle to perform. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4Slice3( ReadOnlySpan source, - Span dest, + Span destination, TShuffle shuffle) where TShuffle : struct, IShuffle4Slice3 { - VerifyShuffle4Slice3SpanInput(source, dest); + VerifyShuffle4Slice3SpanInput(source, destination); - shuffle.ShuffleReduce(ref source, ref dest); + shuffle.ShuffleReduce(ref source, ref destination); // Deal with the remainder: if (source.Length > 0) { - shuffle.RunFallbackShuffle(source, dest); + shuffle.Shuffle(source, destination); } } private static void Shuffle4Remainder( ReadOnlySpan source, - Span dest, + Span destination, byte control) { ref float sBase = ref MemoryMarshal.GetReference(source); - ref float dBase = ref MemoryMarshal.GetReference(dest); + ref float dBase = ref MemoryMarshal.GetReference(destination); Shuffle.InverseMMShuffle(control, out uint p3, out uint p2, out uint p1, out uint p0); for (nuint i = 0; i < (uint)source.Length; i += 4) @@ -158,69 +158,69 @@ internal static partial class SimdUtils } [Conditional("DEBUG")] - internal static void VerifyShuffle4SpanInput(ReadOnlySpan source, Span dest) + internal static void VerifyShuffle4SpanInput(ReadOnlySpan source, Span destination) where T : struct { DebugGuard.IsTrue( - source.Length == dest.Length, + source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); DebugGuard.IsTrue( source.Length % 4 == 0, nameof(source), - "Input spans must be divisable by 4!"); + "Input spans must be divisible by 4!"); } [Conditional("DEBUG")] - private static void VerifyShuffle3SpanInput(ReadOnlySpan source, Span dest) + private static void VerifyShuffle3SpanInput(ReadOnlySpan source, Span destination) where T : struct { DebugGuard.IsTrue( - source.Length <= dest.Length, + source.Length <= destination.Length, nameof(source), - "Source should fit into dest!"); + "Source should fit into destination!"); DebugGuard.IsTrue( source.Length % 3 == 0, nameof(source), - "Input spans must be divisable by 3!"); + "Input spans must be divisible by 3!"); } [Conditional("DEBUG")] - private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan source, Span dest) + private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan source, Span destination) { DebugGuard.IsTrue( source.Length % 3 == 0, nameof(source), - "Input span must be divisable by 3!"); + "Input span must be divisible by 3!"); DebugGuard.IsTrue( - dest.Length % 4 == 0, - nameof(dest), - "Output span must be divisable by 4!"); + destination.Length % 4 == 0, + nameof(destination), + "Output span must be divisible by 4!"); DebugGuard.IsTrue( - source.Length == dest.Length * 3 / 4, + source.Length == destination.Length * 3 / 4, nameof(source), "Input span must be 3/4 the length of the output span!"); } [Conditional("DEBUG")] - private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan source, Span dest) + private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan source, Span destination) { DebugGuard.IsTrue( source.Length % 4 == 0, nameof(source), - "Input span must be divisable by 4!"); + "Input span must be divisible by 4!"); DebugGuard.IsTrue( - dest.Length % 3 == 0, - nameof(dest), - "Output span must be divisable by 3!"); + destination.Length % 3 == 0, + nameof(destination), + "Output span must be divisible by 3!"); DebugGuard.IsTrue( - dest.Length >= source.Length * 3 / 4, + destination.Length >= source.Length * 3 / 4, nameof(source), "Output span must be at least 3/4 the length of the input span!"); } @@ -509,6 +509,27 @@ internal static partial class SimdUtils } } + [MethodImpl(InliningOptions.ShortMethod)] + public static void MMShuffleSpan(ref Span span, byte control) + { + InverseMMShuffle( + control, + out uint p3, + out uint p2, + out uint p1, + out uint p0); + + ref int spanBase = ref MemoryMarshal.GetReference(span); + + for (nuint i = 0; i < (uint)span.Length; i += 4) + { + Unsafe.Add(ref spanBase, i + 0) = (int)(p0 + i); + Unsafe.Add(ref spanBase, i + 1) = (int)(p1 + i); + Unsafe.Add(ref spanBase, i + 2) = (int)(p2 + i); + Unsafe.Add(ref spanBase, i + 3) = (int)(p3 + i); + } + } + [MethodImpl(InliningOptions.ShortMethod)] public static void InverseMMShuffle( byte control, diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs index c98be1fcd..927992686 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs index 3855a293c..cff9f47af 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers.Binary; -using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Webp.Chunks; diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs index 5ed7aab1e..c8ff579a8 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Common.Helpers; - namespace SixLabors.ImageSharp.Formats.Webp.Chunks; internal readonly struct WebpFrameData diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs index 70d6870ce..f781d6114 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Common.Helpers; - namespace SixLabors.ImageSharp.Formats.Webp.Chunks; internal readonly struct WebpVp8X diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 518c09ff4..f15cb3eb5 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index 2b74c300a..6e9e4f9cd 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Common/Helpers/RiffHelper.cs b/src/ImageSharp/Formats/Webp/RiffHelper.cs similarity index 98% rename from src/ImageSharp/Common/Helpers/RiffHelper.cs rename to src/ImageSharp/Formats/Webp/RiffHelper.cs index 8f06e5886..d3862ea8b 100644 --- a/src/ImageSharp/Common/Helpers/RiffHelper.cs +++ b/src/ImageSharp/Formats/Webp/RiffHelper.cs @@ -4,7 +4,7 @@ using System.Buffers.Binary; using System.Text; -namespace SixLabors.ImageSharp.Common.Helpers; +namespace SixLabors.ImageSharp.Formats.Webp; internal static class RiffHelper { diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 3442a0807..8616ecb3b 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -28,7 +28,7 @@ internal static partial class Vector4Converters private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); /// - /// Provides an efficient default implementation for + /// Provides an efficient default implementation for /// The method works by internally converting to a therefore it's not applicable for that type! /// [MethodImpl(InliningOptions.ShortMethod)] @@ -72,7 +72,7 @@ internal static partial class Vector4Converters } /// - /// Provides an efficient default implementation for + /// Provides an efficient default implementation for /// The method is works by internally converting to a therefore it's not applicable for that type! /// [MethodImpl(InliningOptions.ShortMethod)] @@ -102,16 +102,14 @@ internal static partial class Vector4Converters // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion, // so let's allocate a temporary buffer as usually: - using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count)) - { - Span tempSpan = tempBuffer.Memory.Span; + using IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count); + Span tempSpan = tempBuffer.Memory.Span; - SimdUtils.NormalizedFloatToByteSaturate( - MemoryMarshal.Cast(sourceVectors), - MemoryMarshal.Cast(tempSpan)); + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + MemoryMarshal.Cast(tempSpan)); - pixelOperations.FromRgba32(configuration, tempSpan, destPixels); - } + pixelOperations.FromRgba32(configuration, tempSpan, destPixels); } private static int CalculateVector4ConversionThreshold() From 4931372733a618bc32f382a05c7231dbafb1b3a1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 13 Jan 2024 15:43:26 +1000 Subject: [PATCH 046/220] Port first Shuffl3 method --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 88 ++++++++--------- .../Common/Helpers/Vector128Utilities.cs | 99 +++++++++++++++++++ 2 files changed, 142 insertions(+), 45 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/Vector128Utilities.cs diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 4732effd4..4b9a90a95 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp; @@ -95,15 +96,15 @@ internal static partial class SimdUtils /// /// Shuffle 8-bit integers - /// using the control and store the results in . + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4Reduce( ref ReadOnlySpan source, - ref Span dest, + ref Span destination, byte control) { if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) @@ -128,29 +129,29 @@ internal static partial class SimdUtils { Shuffle4( source[..adjustedCount], - dest[..adjustedCount], + destination[..adjustedCount], control); source = source[adjustedCount..]; - dest = dest[adjustedCount..]; + destination = destination[adjustedCount..]; } } } /// /// Shuffles 8-bit integer triplets within 128-bit lanes in - /// using the control and store the results in . + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle3Reduce( ref ReadOnlySpan source, - ref Span dest, + ref Span destination, byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) { int remainder = source.Length % (Vector128.Count * 3); @@ -160,11 +161,11 @@ internal static partial class SimdUtils { Shuffle3( source[..adjustedCount], - dest[..adjustedCount], + destination[..adjustedCount], control); source = source[adjustedCount..]; - dest = dest[adjustedCount..]; + destination = destination[adjustedCount..]; } } } @@ -446,24 +447,21 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Shuffle3( ReadOnlySpan source, - Span dest, + Span destination, byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) { - Vector128 vmask = ShuffleMaskPad4Nx16(); - Vector128 vmasko = ShuffleMaskSlice4Nx16(); - Vector128 vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); + Vector128 maskPad4Nx16 = ShuffleMaskPad4Nx16(); + Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); + Vector128 maskE = Vector128Utilities.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12); Span bytes = stackalloc byte[Vector128.Count]; Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); - ref Vector128 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); @@ -472,36 +470,36 @@ internal static partial class SimdUtils ref Vector128 vs = ref Unsafe.Add(ref sourceBase, i); Vector128 v0 = vs; - Vector128 v1 = Unsafe.Add(ref vs, 1); - Vector128 v2 = Unsafe.Add(ref vs, 2); - Vector128 v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); + Vector128 v1 = Unsafe.Add(ref vs, (nuint)1); + Vector128 v2 = Unsafe.Add(ref vs, (nuint)2); + Vector128 v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v2 = Ssse3.AlignRight(v2, v1, 8); - v1 = Ssse3.AlignRight(v1, v0, 12); + v2 = Vector128Utilities.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v1, v0, 12); - v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vmask), vshuffle); - v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vmask), vshuffle); - v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vmask), vshuffle); - v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vmask), vshuffle); + v0 = Vector128.Shuffle(Vector128.Shuffle(v0, maskPad4Nx16), mask); + v1 = Vector128.Shuffle(Vector128.Shuffle(v1, maskPad4Nx16), mask); + v2 = Vector128.Shuffle(Vector128.Shuffle(v2, maskPad4Nx16), mask); + v3 = Vector128.Shuffle(Vector128.Shuffle(v3, maskPad4Nx16), mask); - v0 = Ssse3.Shuffle(v0, vmaske); - v1 = Ssse3.Shuffle(v1, vmasko); - v2 = Ssse3.Shuffle(v2, vmaske); - v3 = Ssse3.Shuffle(v3, vmasko); + v0 = Vector128.Shuffle(v0, maskE); + v1 = Vector128.Shuffle(v1, maskSlice4Nx16); + v2 = Vector128.Shuffle(v2, maskE); + v3 = Vector128.Shuffle(v3, maskSlice4Nx16); - v0 = Ssse3.AlignRight(v1, v0, 4); - v3 = Ssse3.AlignRight(v3, v2, 12); + v0 = Vector128Utilities.AlignRight(v1, v0, 4); + v3 = Vector128Utilities.AlignRight(v3, v2, 12); - v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); - v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); + v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4); + v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v1 = Ssse3.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v2, v1, 8); - ref Vector128 vd = ref Unsafe.Add(ref destBase, i); + ref Vector128 vd = ref Unsafe.Add(ref destinationBase, i); vd = v0; - Unsafe.Add(ref vd, 1) = v1; - Unsafe.Add(ref vd, 2) = v3; + Unsafe.Add(ref vd, (nuint)1) = v1; + Unsafe.Add(ref vd, (nuint)2) = v3; } } } @@ -509,7 +507,7 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Pad3Shuffle4( ReadOnlySpan source, - Span dest, + Span destination, byte control) { if (Ssse3.IsSupported) @@ -525,7 +523,7 @@ internal static partial class SimdUtils ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs new file mode 100644 index 000000000..829362da8 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -0,0 +1,99 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Common.Helpers; + +/// +/// Defines utility methods for that have not yet been normalized in the runtime. +/// Should only be used if the intrinsics are available. +/// +internal static class Vector128Utilities +{ + /// + /// Gets a value indicating whether right shift operations are supported. + /// + public static bool SupportsRightShift + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Ssse3.IsSupported || AdvSimd.IsSupported; + } + + /// + /// Shifts a 128-bit value right by a specified number of bytes while shifting in zeros. + /// + /// The value to shift. + /// The number of bytes to shift by. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 ShiftRightBytesInVector(Vector128 value, [ConstantExpected(Max = (byte)15)] byte numBytes) + { + if (Sse2.IsSupported) + { + return Sse2.ShiftRightLogical128BitLane(value, numBytes); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ExtractVector128(value, Vector128.Zero, numBytes); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Shifts a 128-bit value left by a specified number of bytes while shifting in zeros. + /// + /// The value to shift. + /// The number of bytes to shift by. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 ShiftLeftBytesInVector(Vector128 value, [ConstantExpected(Max = (byte)15)] byte numBytes) + { + if (Sse2.IsSupported) + { + return Sse2.ShiftLeftLogical128BitLane(value, numBytes); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ExtractVector128(Vector128.Zero, value, numBytes); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Right aligns elements of two source 128-bit values depending on bits in a mask. + /// + /// The left hand source vector. + /// The right hand source vector. + /// An 8-bit mask used for the operation. + /// The . + public static Vector128 AlignRight(Vector128 left, Vector128 right, [ConstantExpected(Max = (byte)15)] byte mask) + { + if (Sse3.IsSupported) + { + return Ssse3.AlignRight(left, right, mask); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ExtractVector128(right, left, mask); + } + + ThrowUnreachableException(); + return default; + } + + [DoesNotReturn] + private static void ThrowUnreachableException() => throw new UnreachableException(); +} From ce73c49fcba4c0411d717887d6644998d30f4016 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 13 Jan 2024 19:14:05 +0100 Subject: [PATCH 047/220] Update 32 bit test images without alpha bits --- tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs | 4 +--- tests/Images/Input/Tga/32bit_no_alphabits.tga | 4 ++-- tests/Images/Input/Tga/32bit_rle_no_alphabits.tga | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 9efbac6a3..dbd7885e5 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -723,10 +723,8 @@ public class TgaDecoderTests { using (Image image = provider.GetImage(TgaDecoder.Instance)) { - // Using here the reference output instead of the reference decoder, - // because the reference decoder does not ignore the alpha data here. image.DebugSave(provider); - image.CompareToReferenceOutput(ImageComparer.Exact, provider); + ImageComparingUtils.CompareWithReferenceDecoder(provider, image); } } diff --git a/tests/Images/Input/Tga/32bit_no_alphabits.tga b/tests/Images/Input/Tga/32bit_no_alphabits.tga index 903eca459..206e8d7c5 100644 --- a/tests/Images/Input/Tga/32bit_no_alphabits.tga +++ b/tests/Images/Input/Tga/32bit_no_alphabits.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aea1128a1bd7477dfa0d007a1eba25907be24847284c48a5f9fbd61bcea3cf0 -size 61522 +oid sha256:019315f9dcbe4516ecb15426a45c210d437e9ad152c8e1a0e80abe9449177e12 +size 235218 diff --git a/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga b/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga index b21dad5e0..153b0a055 100644 --- a/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga +++ b/tests/Images/Input/Tga/32bit_rle_no_alphabits.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98a198392bd527523f8649d6126af81e5a588ad7265dc3d48a1da7b5a6cb6ff7 -size 230578 +oid sha256:33954ae93b4c7d57f52965a9028e97119c546db1da255100c2903a2760c7479e +size 76870 From 10b27a3e29d80311648964887af2d6d0dcdd5454 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 14 Jan 2024 10:36:22 +0100 Subject: [PATCH 048/220] Remove no longer needed 32 bit tga reference images --- ...CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_no_alphabits.png | 3 --- ...ecode_WhenAlphaBitsNotSet_Rgba32_32bit_rle_no_alphabits.png | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_no_alphabits.png delete mode 100644 tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_rle_no_alphabits.png diff --git a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_no_alphabits.png b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_no_alphabits.png deleted file mode 100644 index e12985f7a..000000000 --- a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_no_alphabits.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d3dc0516f656c14b5ffcc40f88d3912f2a8fb310dfbda5836e15847e205919b5 -size 1012 diff --git a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_rle_no_alphabits.png b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_rle_no_alphabits.png deleted file mode 100644 index 726721824..000000000 --- a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_CanDecode_WhenAlphaBitsNotSet_Rgba32_32bit_rle_no_alphabits.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cad24c7e4657f2bc8d8a60ea76397eac0adf8dee5fc81f60bc5bc02dd7eeed8f -size 90589 From b250ac3923c86868e1a1694054825bc66c2b1553 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 14:30:16 +1000 Subject: [PATCH 049/220] Introduce new utilities to replace poor performing runtime methods --- .../Helpers/Shuffle/IComponentShuffle.cs | 173 +------------ .../Common/Helpers/Shuffle/IPad3Shuffle4.cs | 26 +- .../Common/Helpers/Shuffle/IShuffle3.cs | 18 +- .../Common/Helpers/Shuffle/IShuffle4.cs | 178 ++++++++++++++ .../Common/Helpers/Shuffle/IShuffle4Slice3.cs | 28 +-- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 230 +++++++++--------- .../Common/Helpers/Vector128Utilities.cs | 83 ++++++- .../Common/Helpers/Vector256Utilities.cs | 78 ++++++ .../Common/Helpers/Vector512Utilities.cs | 80 ++++++ 9 files changed, 563 insertions(+), 331 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs create mode 100644 src/ImageSharp/Common/Helpers/Vector256Utilities.cs create mode 100644 src/ImageSharp/Common/Helpers/Vector512Utilities.cs diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs index d4ab8c618..c856267db 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs @@ -1,12 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using static SixLabors.ImageSharp.SimdUtils; - // The JIT can detect and optimize rotation idioms ROTL (Rotate Left) // and ROTR (Rotate Right) emitting efficient CPU instructions: // https://github.com/dotnet/coreclr/pull/1830 @@ -20,7 +14,7 @@ internal interface IComponentShuffle { /// /// Shuffles then slices 8-bit integers in - /// using the control and store the results in . + /// using a byte control and store the results in . /// If successful, this method will reduce the length of length /// by the shuffle amount. /// @@ -40,168 +34,3 @@ internal interface IComponentShuffle /// void Shuffle(ReadOnlySpan source, Span destination); } - -/// -internal interface IShuffle4 : IComponentShuffle -{ -} - -internal readonly struct DefaultShuffle4(byte control) : IShuffle4 -{ - public byte Control { get; } = control; - - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(destination); - - SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); - - for (nuint i = 0; i < (uint)source.Length; i += 4) - { - Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); - Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); - Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); - Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); - } - } -} - -internal readonly struct WXYZShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // ROTL(8, packed) = [Z Y X W] - Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24); - } - } -} - -internal readonly struct WZYXShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // REVERSE(packedArgb) = [X Y Z W] - Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed); - } - } -} - -internal readonly struct YZWXShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // ROTR(8, packedArgb) = [Y Z W X] - Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8); - } - } -} - -internal readonly struct ZYXWShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // tmp1 = [W 0 Y 0] - // tmp2 = [0 Z 0 X] - // tmp3=ROTL(16, tmp2) = [0 X 0 Z] - // tmp1 + tmp3 = [W X Y Z] - uint tmp1 = packed & 0xFF00FF00; - uint tmp2 = packed & 0x00FF00FF; - uint tmp3 = BitOperations.RotateLeft(tmp2, 16); - - Unsafe.Add(ref dBase, i) = tmp1 + tmp3; - } - } -} - -internal readonly struct XWZYShuffle4 : IShuffle4 -{ - [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) - => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230); - - [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span destination) - { - ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - uint n = (uint)source.Length / 4; - - for (nuint i = 0; i < n; i++) - { - uint packed = Unsafe.Add(ref sBase, i); - - // packed = [W Z Y X] - // tmp1 = [0 Z 0 X] - // tmp2 = [W 0 Y 0] - // tmp3=ROTL(16, tmp2) = [Y 0 W 0] - // tmp1 + tmp3 = [Y Z W X] - uint tmp1 = packed & 0x00FF00FF; - uint tmp2 = packed & 0xFF00FF00; - uint tmp3 = BitOperations.RotateLeft(tmp2, 16); - - Unsafe.Add(ref dBase, i) = tmp1 + tmp3; - } - } -} diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs index 255448d61..0f282c7f9 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,22 +13,21 @@ internal interface IPad3Shuffle4 : IComponentShuffle { } -internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 +internal readonly struct DefaultPad3Shuffle4([ConstantExpected] byte control) : IPad3Shuffle4 { - public DefaultPad3Shuffle4(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); @@ -51,14 +51,14 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length); ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs index 89faca243..3c0973ad6 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,22 +13,21 @@ internal interface IShuffle3 : IComponentShuffle { } -internal readonly struct DefaultShuffle3 : IShuffle3 +internal readonly struct DefaultShuffle3([ConstantExpected] byte control) : IShuffle3 { - public DefaultShuffle3(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle3Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs new file mode 100644 index 000000000..d5c6df2c8 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs @@ -0,0 +1,178 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; + +namespace SixLabors.ImageSharp; + +/// +internal interface IShuffle4 : IComponentShuffle +{ +} + +internal readonly struct DefaultShuffle4([ConstantExpected] byte control) : IShuffle4 +{ + public byte Control { get; } = control; + + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref byte sBase = ref MemoryMarshal.GetReference(source); + ref byte dBase = ref MemoryMarshal.GetReference(destination); + + SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); + + for (nuint i = 0; i < (uint)source.Length; i += 4) + { + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); + Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); + Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); + Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); + } + } +} + +internal readonly struct WXYZShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // ROTL(8, packed) = [Z Y X W] + Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24); + } + } +} + +internal readonly struct WZYXShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // REVERSE(packedArgb) = [X Y Z W] + Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed); + } + } +} + +internal readonly struct YZWXShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // ROTR(8, packedArgb) = [Y Z W X] + Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8); + } + } +} + +internal readonly struct ZYXWShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // tmp1 = [W 0 Y 0] + // tmp2 = [0 Z 0 X] + // tmp3=ROTL(16, tmp2) = [0 X 0 Z] + // tmp1 + tmp3 = [W X Y Z] + uint tmp1 = packed & 0xFF00FF00; + uint tmp2 = packed & 0x00FF00FF; + uint tmp3 = BitOperations.RotateLeft(tmp2, 16); + + Unsafe.Add(ref dBase, i) = tmp1 + tmp3; + } + } +} + +internal readonly struct XWZYShuffle4 : IShuffle4 +{ + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230); + + [MethodImpl(InliningOptions.ShortMethod)] + public void Shuffle(ReadOnlySpan source, Span destination) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + uint n = (uint)source.Length / 4; + + for (nuint i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // tmp1 = [0 Z 0 X] + // tmp2 = [W 0 Y 0] + // tmp3=ROTL(16, tmp2) = [Y 0 W 0] + // tmp1 + tmp3 = [Y Z W X] + uint tmp1 = packed & 0x00FF00FF; + uint tmp2 = packed & 0xFF00FF00; + uint tmp3 = BitOperations.RotateLeft(tmp2, 16); + + Unsafe.Add(ref dBase, i) = tmp1 + tmp3; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs index 30fda7a8e..3e7e44066 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static SixLabors.ImageSharp.SimdUtils; @@ -12,26 +13,25 @@ internal interface IShuffle4Slice3 : IComponentShuffle { } -internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 +internal readonly struct DefaultShuffle4Slice3([ConstantExpected] byte control) : IShuffle4Slice3 { - public DefaultShuffle4Slice3(byte control) - => this.Control = control; - - public byte Control { get; } + public byte Control { get; } = control; [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) +#pragma warning disable CA1857 // A constant is expected for the parameter + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, this.Control); +#pragma warning restore CA1857 // A constant is expected for the parameter [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); + ref byte dBase = ref MemoryMarshal.GetReference(destination); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); - for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4) + for (nuint i = 0, j = 0; i < (uint)destination.Length; i += 3, j += 4) { Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j); @@ -43,14 +43,14 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 { [MethodImpl(InliningOptions.ShortMethod)] - public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) - => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210); + public void ShuffleReduce(ref ReadOnlySpan source, ref Span destination) + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] - public void Shuffle(ReadOnlySpan source, Span dest) + public void Shuffle(ReadOnlySpan source, Span destination) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); nint n = (nint)(uint)source.Length / 4; nint m = Numerics.Modulo4(n); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 4b9a90a95..14d2f1c25 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -63,7 +63,9 @@ internal static partial class SimdUtils ref Span destination, [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) + if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) || + (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) || + (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat)) { int remainder = 0; if (Vector512.IsHardwareAccelerated) @@ -105,9 +107,11 @@ internal static partial class SimdUtils public static void Shuffle4Reduce( ref ReadOnlySpan source, ref Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated) + if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) || + (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) || + (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte)) { int remainder = 0; if (Vector512.IsHardwareAccelerated) @@ -139,7 +143,7 @@ internal static partial class SimdUtils } /// - /// Shuffles 8-bit integer triplets within 128-bit lanes in + /// Shuffles 8-bit integer triplets in /// using the control and store the results in . /// /// The source span of bytes. @@ -149,9 +153,9 @@ internal static partial class SimdUtils public static void Shuffle3Reduce( ref ReadOnlySpan source, ref Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign) { int remainder = source.Length % (Vector128.Count * 3); @@ -171,67 +175,67 @@ internal static partial class SimdUtils } /// - /// Pads then shuffles 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Pads then shuffles 8-bit integers in + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Pad3Shuffle4Reduce( ref ReadOnlySpan source, - ref Span dest, - byte control) + ref Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { int remainder = source.Length % (Vector128.Count * 3); int sourceCount = source.Length - remainder; - int destCount = (int)((uint)sourceCount * 4 / 3); + int destinationCount = (int)((uint)sourceCount * 4 / 3); if (sourceCount > 0) { Pad3Shuffle4( source[..sourceCount], - dest[..destCount], + destination[..destinationCount], control); source = source[sourceCount..]; - dest = dest[destCount..]; + destination = destination[destinationCount..]; } } } /// - /// Shuffles then slices 8-bit integers within 128-bit lanes in - /// using the control and store the results in . + /// Shuffles then slices 8-bit integers in + /// using the control and store the results in . /// /// The source span of bytes. - /// The destination span of bytes. + /// The destination span of bytes. /// The byte control. [MethodImpl(InliningOptions.ShortMethod)] public static void Shuffle4Slice3Reduce( ref ReadOnlySpan source, - ref Span dest, - byte control) + ref Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { int remainder = source.Length & ((Vector128.Count * 4) - 1); // bit-hack for modulo int sourceCount = source.Length - remainder; - int destCount = (int)((uint)sourceCount * 3 / 4); + int destinationCount = (int)((uint)sourceCount * 3 / 4); if (sourceCount > 0) { Shuffle4Slice3( source[..sourceCount], - dest[..destCount], + destination[..destinationCount], control); source = source[sourceCount..]; - dest = dest[destCount..]; + destination = destination[destinationCount..]; } } } @@ -242,12 +246,8 @@ internal static partial class SimdUtils Span destination, [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated) + if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector512.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector512 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -260,26 +260,22 @@ internal static partial class SimdUtils ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector512.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector512Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } - else if (Vector256.IsHardwareAccelerated) + else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector256.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -292,26 +288,22 @@ internal static partial class SimdUtils ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector256.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector256Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } - else if (Vector128.IsHardwareAccelerated) + else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat) { - Span temp = stackalloc int[Vector128.Count]; - Shuffle.MMShuffleSpan(ref temp, control); - Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); - ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); @@ -324,17 +316,17 @@ internal static partial class SimdUtils ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector128.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector128Utilities.Shuffle(vs0, control); + Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control); + Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control); + Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control); } } } @@ -344,9 +336,9 @@ internal static partial class SimdUtils private static void Shuffle4( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector512.IsHardwareAccelerated) + if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector512.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -364,21 +356,21 @@ internal static partial class SimdUtils ref Vector512 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector512 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector512.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector512Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else if (Vector256.IsHardwareAccelerated) + else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector256.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -396,21 +388,21 @@ internal static partial class SimdUtils ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector256.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector256Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } - else if (Vector128.IsHardwareAccelerated) + else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte) { Span temp = stackalloc byte[Vector128.Count]; Shuffle.MMShuffleSpan(ref temp, control); @@ -428,17 +420,17 @@ internal static partial class SimdUtils ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128 vd0 = ref Unsafe.Add(ref destinationBase, i); - vd0 = Vector128.Shuffle(vs0, mask); - Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); - Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); - Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); + vd0 = Vector128Utilities.Shuffle(vs0, mask); + Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask); + Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask); + Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask); } if (m > 0) { for (nuint i = u; i < n; i++) { - Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask); + Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask); } } } @@ -448,9 +440,9 @@ internal static partial class SimdUtils private static void Shuffle3( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign) { Vector128 maskPad4Nx16 = ShuffleMaskPad4Nx16(); Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); @@ -477,15 +469,15 @@ internal static partial class SimdUtils v2 = Vector128Utilities.AlignRight(v2, v1, 8); v1 = Vector128Utilities.AlignRight(v1, v0, 12); - v0 = Vector128.Shuffle(Vector128.Shuffle(v0, maskPad4Nx16), mask); - v1 = Vector128.Shuffle(Vector128.Shuffle(v1, maskPad4Nx16), mask); - v2 = Vector128.Shuffle(Vector128.Shuffle(v2, maskPad4Nx16), mask); - v3 = Vector128.Shuffle(Vector128.Shuffle(v3, maskPad4Nx16), mask); + v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16), mask); + v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16), mask); + v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16), mask); + v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16), mask); - v0 = Vector128.Shuffle(v0, maskE); - v1 = Vector128.Shuffle(v1, maskSlice4Nx16); - v2 = Vector128.Shuffle(v2, maskE); - v3 = Vector128.Shuffle(v3, maskSlice4Nx16); + v0 = Vector128Utilities.Shuffle(v0, maskE); + v1 = Vector128Utilities.Shuffle(v1, maskSlice4Nx16); + v2 = Vector128Utilities.Shuffle(v2, maskE); + v3 = Vector128Utilities.Shuffle(v3, maskSlice4Nx16); v0 = Vector128Utilities.AlignRight(v1, v0, 4); v3 = Vector128Utilities.AlignRight(v3, v2, 12); @@ -508,21 +500,21 @@ internal static partial class SimdUtils private static void Pad3Shuffle4( ReadOnlySpan source, Span destination, - byte control) + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { - Vector128 vmask = ShuffleMaskPad4Nx16(); - Vector128 vfill = Vector128.Create(0xff000000ff000000ul).AsByte(); + Vector128 maskPad4Nx16 = ShuffleMaskPad4Nx16(); + Vector128 fill = Vector128.Create(0xff000000ff000000ul).AsByte(); - Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Span temp = stackalloc byte[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector128 destBase = + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); @@ -532,17 +524,17 @@ internal static partial class SimdUtils ref Vector128 v0 = ref Unsafe.Add(ref sourceBase, i); Vector128 v1 = Unsafe.Add(ref v0, 1); Vector128 v2 = Unsafe.Add(ref v0, 2); - Vector128 v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); + Vector128 v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v2 = Ssse3.AlignRight(v2, v1, 8); - v1 = Ssse3.AlignRight(v1, v0, 12); + v2 = Vector128Utilities.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v1, v0, 12); - ref Vector128 vd = ref Unsafe.Add(ref destBase, j); + ref Vector128 vd = ref Unsafe.Add(ref destinationBase, j); - vd = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v0, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v1, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v2, vmask), vfill), vshuffle); - Unsafe.Add(ref vd, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v3, vmask), vfill), vshuffle); + vd = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 1) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 2) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16) | fill, mask); + Unsafe.Add(ref vd, 3) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16) | fill, mask); } } } @@ -550,23 +542,23 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ShortMethod)] private static void Shuffle4Slice3( ReadOnlySpan source, - Span dest, - byte control) + Span destination, + [ConstantExpected] byte control) { - if (Ssse3.IsSupported) + if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { - Vector128 vmasko = ShuffleMaskSlice4Nx16(); - Vector128 vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); + Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); + Vector128 maskE = Ssse3.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12); - Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MMShuffleSpan(ref bytes, control); - Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); + Span temp = stackalloc byte[Vector128.Count]; + Shuffle.MMShuffleSpan(ref temp, control); + Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(temp)); ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector128 destinationBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); nuint n = source.Vector128Count(); @@ -579,20 +571,20 @@ internal static partial class SimdUtils Vector128 v2 = Unsafe.Add(ref vs, 2); Vector128 v3 = Unsafe.Add(ref vs, 3); - v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vshuffle), vmaske); - v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vshuffle), vmasko); - v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vshuffle), vmaske); - v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vshuffle), vmasko); + v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, mask), maskE); + v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, mask), maskSlice4Nx16); + v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, mask), maskE); + v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, mask), maskSlice4Nx16); - v0 = Ssse3.AlignRight(v1, v0, 4); - v3 = Ssse3.AlignRight(v3, v2, 12); + v0 = Vector128Utilities.AlignRight(v1, v0, 4); + v3 = Vector128Utilities.AlignRight(v3, v2, 12); - v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); - v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); + v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4); + v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4); - v1 = Ssse3.AlignRight(v2, v1, 8); + v1 = Vector128Utilities.AlignRight(v2, v1, 8); - ref Vector128 vd = ref Unsafe.Add(ref destBase, j); + ref Vector128 vd = ref Unsafe.Add(ref destinationBase, j); vd = v0; Unsafe.Add(ref vd, 1) = v1; diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 829362da8..a272e459c 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -11,20 +11,95 @@ using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Common.Helpers; /// -/// Defines utility methods for that have not yet been normalized in the runtime. +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// /// Should only be used if the intrinsics are available. /// internal static class Vector128Utilities { /// - /// Gets a value indicating whether right shift operations are supported. + /// Gets a value indicating whether shuffle operations are supported. /// - public static bool SupportsRightShift + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Sse.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Ssse3.IsSupported || AdvSimd.Arm64.IsSupported; + } + + /// + /// Gets a value indicating whether right align operations are supported. + /// + public static bool SupportsRightAlign { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Ssse3.IsSupported || AdvSimd.IsSupported; } + /// + /// Gets a value indicating whether right or left byte shift operations are supported. + /// + public static bool SupportsShiftByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Sse2.IsSupported || AdvSimd.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using the control. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + public static Vector128 Shuffle(Vector128 vector, [ConstantExpected] byte control) + { + if (Sse.IsSupported) + { + return Sse.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// + /// A new vector containing the values from selected by the given . + /// + public static Vector128 Shuffle(Vector128 vector, Vector128 indices) + { + if (Ssse3.IsSupported) + { + return Ssse3.Shuffle(vector, indices); + } + + if (AdvSimd.Arm64.IsSupported) + { + return AdvSimd.Arm64.VectorTableLookup(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + /// /// Shifts a 128-bit value right by a specified number of bytes while shifting in zeros. /// @@ -80,7 +155,7 @@ internal static class Vector128Utilities /// The . public static Vector128 AlignRight(Vector128 left, Vector128 right, [ConstantExpected(Max = (byte)15)] byte mask) { - if (Sse3.IsSupported) + if (Ssse3.IsSupported) { return Ssse3.AlignRight(left, right, mask); } diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs new file mode 100644 index 000000000..14fa24b31 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Common.Helpers; + +/// +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// +/// Should only be used if the intrinsics are available. +/// +internal static class Vector256Utilities +{ + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx2.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + public static Vector256 Shuffle(Vector256 vector, [ConstantExpected] byte control) + { + if (Avx.IsSupported) + { + return Avx.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// The . + public static Vector256 Shuffle(Vector256 vector, Vector256 indices) + { + if (Avx2.IsSupported) + { + return Avx2.Shuffle(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + + [DoesNotReturn] + private static void ThrowUnreachableException() => throw new UnreachableException(); +} diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs new file mode 100644 index 000000000..5488b4064 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Common.Helpers; + +/// +/// Defines utility methods for that have either: +/// +/// Not yet been normalized in the runtime. +/// Produce codegen that is poorly optimized by the runtime. +/// +/// Should only be used if the intrinsics are available. +/// +internal static class Vector512Utilities +{ + /// + /// Gets a value indicating whether shuffle float operations are supported. + /// + public static bool SupportsShuffleFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx512F.IsSupported; + } + + /// + /// Gets a value indicating whether shuffle byte operations are supported. + /// + public static bool SupportsShuffleByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Avx512BW.IsSupported; + } + + /// + /// Creates a new vector by selecting values from an input vector using the control. + /// + /// The input vector from which values are selected. + /// The shuffle control byte. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Shuffle(Vector512 vector, [ConstantExpected] byte control) + { + if (Avx512F.IsSupported) + { + return Avx512F.Shuffle(vector, vector, control); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Creates a new vector by selecting values from an input vector using a set of indices. + /// + /// The input vector from which values are selected. + /// + /// The per-element indices used to select a value from . + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Shuffle(Vector512 vector, Vector512 indices) + { + if (Avx512BW.IsSupported) + { + return Avx512BW.Shuffle(vector, indices); + } + + ThrowUnreachableException(); + return default; + } + + [DoesNotReturn] + private static void ThrowUnreachableException() => throw new UnreachableException(); +} From 4c47a7881b75caa57ee8aeaf2eb412d2cdd5a728 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 14:47:04 +1000 Subject: [PATCH 050/220] Fix missed normalization --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 14d2f1c25..f27852a82 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -548,7 +548,7 @@ internal static partial class SimdUtils if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte) { Vector128 maskSlice4Nx16 = ShuffleMaskSlice4Nx16(); - Vector128 maskE = Ssse3.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12); + Vector128 maskE = Vector128Utilities.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12); Span temp = stackalloc byte[Vector128.Count]; Shuffle.MMShuffleSpan(ref temp, control); From d9126ea99189408cc80d2e84c8e03ca8917cc2f5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 15:24:01 +1000 Subject: [PATCH 051/220] Attempt to fix left shift --- src/ImageSharp/Common/Helpers/Vector128Utilities.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index a272e459c..3ca551494 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -111,7 +111,9 @@ internal static class Vector128Utilities { if (Sse2.IsSupported) { - return Sse2.ShiftRightLogical128BitLane(value, numBytes); +#pragma warning disable CA1857 // A constant is expected for the parameter + return Sse2.ShiftRightLogical128BitLane(value, (byte)(16 - numBytes)); +#pragma warning restore CA1857 // A constant is expected for the parameter } if (AdvSimd.IsSupported) From 5cf8f606e2f94aeb7df1acc91d6ebb0d7c4fdf54 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 16:46:30 +1000 Subject: [PATCH 052/220] Push the right fix! --- src/ImageSharp/Common/Helpers/Vector128Utilities.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 3ca551494..1290229d9 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -111,9 +111,7 @@ internal static class Vector128Utilities { if (Sse2.IsSupported) { -#pragma warning disable CA1857 // A constant is expected for the parameter - return Sse2.ShiftRightLogical128BitLane(value, (byte)(16 - numBytes)); -#pragma warning restore CA1857 // A constant is expected for the parameter + return Sse2.ShiftRightLogical128BitLane(value, numBytes); } if (AdvSimd.IsSupported) @@ -141,7 +139,9 @@ internal static class Vector128Utilities if (AdvSimd.IsSupported) { - return AdvSimd.ExtractVector128(Vector128.Zero, value, numBytes); +#pragma warning disable CA1857 // A constant is expected for the parameter + return AdvSimd.ExtractVector128(Vector128.Zero, value, (byte)(16 - numBytes)); +#pragma warning restore CA1857 // A constant is expected for the parameter } ThrowUnreachableException(); From c9b4edda19f648706b8938b2286bb05383e5fb56 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 15 Jan 2024 17:18:31 +1000 Subject: [PATCH 053/220] Use proper constant --- src/ImageSharp/Common/Helpers/Vector128Utilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 1290229d9..981f9a47f 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -140,7 +140,7 @@ internal static class Vector128Utilities if (AdvSimd.IsSupported) { #pragma warning disable CA1857 // A constant is expected for the parameter - return AdvSimd.ExtractVector128(Vector128.Zero, value, (byte)(16 - numBytes)); + return AdvSimd.ExtractVector128(Vector128.Zero, value, (byte)(Vector128.Count - numBytes)); #pragma warning restore CA1857 // A constant is expected for the parameter } From acaebd94d767de3d52a8fbee1d0eedd83a0423b9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 16 Jan 2024 21:49:33 +1000 Subject: [PATCH 054/220] Update individual pixel implementations --- src/ImageSharp/Color/Color.cs | 6 +- .../Common/Helpers/ColorNumerics.cs | 37 ++- .../PixelFormats/IPackedVector{TPacked}.cs | 2 +- src/ImageSharp/PixelFormats/IPixel.cs | 116 ++++--- .../PixelFormats/PixelImplementations/A8.cs | 98 +++--- .../PixelImplementations/Abgr32.cs | 238 +++++--------- .../PixelImplementations/Argb32.cs | 242 +++++--------- .../PixelImplementations/Bgr24.cs | 199 +++++------- .../PixelImplementations/Bgr565.cs | 116 ++----- .../PixelImplementations/Bgra32.cs | 206 ++++-------- .../PixelImplementations/Bgra4444.cs | 110 ++----- .../PixelImplementations/Bgra5551.cs | 112 ++----- .../PixelImplementations/Byte4.cs | 125 +++----- .../PixelImplementations/HalfSingle.cs | 114 ++----- .../PixelImplementations/HalfVector2.cs | 110 ++----- .../PixelImplementations/HalfVector4.cs | 118 ++----- .../PixelFormats/PixelImplementations/L16.cs | 150 ++++----- .../PixelFormats/PixelImplementations/L8.cs | 144 ++++----- .../PixelFormats/PixelImplementations/La16.cs | 214 +++++-------- .../PixelFormats/PixelImplementations/La32.cs | 237 ++++++-------- .../PixelImplementations/NormalizedByte2.cs | 131 ++------ .../PixelImplementations/NormalizedByte4.cs | 128 ++------ .../PixelImplementations/NormalizedShort2.cs | 126 ++------ .../PixelImplementations/NormalizedShort4.cs | 124 ++----- .../RgbaVector.PixelOperations.cs | 4 +- .../PixelFormats/PixelImplementations/Rg32.cs | 119 ++++--- .../PixelImplementations/Rgb24.cs | 225 +++++-------- .../PixelImplementations/Rgb48.cs | 161 ++++------ .../PixelImplementations/Rgba1010102.cs | 106 ++---- .../PixelImplementations/Rgba32.cs | 269 ++++++---------- .../PixelImplementations/Rgba64.cs | 302 +++++++----------- .../PixelImplementations/RgbaVector.cs | 146 +++------ .../PixelImplementations/Short2.cs | 126 ++------ .../PixelImplementations/Short4.cs | 134 ++------ src/ImageSharp/PixelFormats/README.md | 2 +- .../PixelConversion_ConvertFromRgba32.cs | 26 +- .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 4 +- .../PixelFormats/La32Tests.cs | 4 +- ...ConverterTests.ReferenceImplementations.cs | 2 +- 39 files changed, 1637 insertions(+), 3196 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index e61abf86f..3b91a78d1 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -98,10 +98,8 @@ public readonly partial struct Color : IEquatable { return new(pixel.ToScaledVector4()); } - else - { - return new(pixel); - } + + return new(pixel); } /// diff --git a/src/ImageSharp/Common/Helpers/ColorNumerics.cs b/src/ImageSharp/Common/Helpers/ColorNumerics.cs index 553a7c2e8..1c30d857f 100644 --- a/src/ImageSharp/Common/Helpers/ColorNumerics.cs +++ b/src/ImageSharp/Common/Helpers/ColorNumerics.cs @@ -41,6 +41,34 @@ internal static class ColorNumerics public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); + /// + /// Gets the luminance from the rgb components using the formula + /// as specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Get8BitBT709Luminance(ushort r, ushort g, ushort b) + => (byte)((From16BitTo8Bit(r) * .2126F) + + (From16BitTo8Bit(g) * .7152F) + + (From16BitTo8Bit(b) * .0722F) + 0.5F); + + /// + /// Gets the luminance from the rgb components using the formula as + /// specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Get16BitBT709Luminance(byte r, byte g, byte b) + => (ushort)((From8BitTo16Bit(r) * .2126F) + + (From8BitTo16Bit(g) * .7152F) + + (From8BitTo16Bit(b) * .0722F) + 0.5F); + /// /// Gets the luminance from the rgb components using the formula as /// specified by ITU-R Recommendation BT.709. @@ -72,8 +100,8 @@ internal static class ColorNumerics /// The 8 bit component value. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte DownScaleFrom16BitTo8Bit(ushort component) - { + public static byte From16BitTo8Bit(ushort component) => + // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is: // // (V * 255) / 65535 @@ -102,8 +130,7 @@ internal static class ColorNumerics // An alternative arithmetic calculation which also gives no errors is: // // (V * 255 + 32895) >> 16 - return (byte)(((component * 255) + 32895) >> 16); - } + (byte)(((component * 255) + 32895) >> 16); /// /// Scales a value from an 8 bit to @@ -112,7 +139,7 @@ internal static class ColorNumerics /// The 8 bit component value. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort UpscaleFrom8BitTo16Bit(byte component) + public static ushort From8BitTo16Bit(byte component) => (ushort)(component * 257); /// diff --git a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs index b74c0ff44..18a8df348 100644 --- a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs +++ b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index b28911a90..f9d88deac 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -14,135 +14,151 @@ namespace SixLabors.ImageSharp.PixelFormats; public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { - /// - /// Gets the pixel type information. - /// - /// The . -#pragma warning disable CA1000 - static abstract PixelTypeInfo GetPixelTypeInfo(); -#pragma warning restore CA1000 - /// /// Creates a instance for this pixel type. /// This method is not intended to be consumed directly. Use instead. /// /// The instance. PixelOperations CreatePixelOperations(); -} -/// -/// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type. -/// -public interface IPixel -{ - /// - /// Initializes the pixel instance from a generic ("scaled") . - /// - /// The vector to load the pixel from. - void FromScaledVector4(Vector4 vector); +#pragma warning disable CA1000 // Do not declare static members on generic types /// - /// Expands the pixel into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. - /// The vector components are typically expanded in least to greatest significance order. + /// Gets the pixel type information. /// - /// The . - Vector4 ToScaledVector4(); + /// The . + static abstract PixelTypeInfo GetPixelTypeInfo(); /// - /// Initializes the pixel instance from a which is specific to the current pixel type. + /// Initializes the pixel instance from a generic scaled . /// - /// The vector to load the pixel from. - void FromVector4(Vector4 vector); + /// The vector to load the pixel from. + /// The . + static abstract TSelf FromScaledVector4(Vector4 source); /// - /// Expands the pixel into a which is specific to the current pixel type. - /// The vector components are typically expanded in least to greatest significance order. + /// Initializes the pixel instance from a which is specific to the current pixel type. /// - /// The . - Vector4 ToVector4(); + /// The vector to load the pixel from. + /// The . + static abstract TSelf FromVector4(Vector4 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromArgb32(Argb32 source); + /// The . + static virtual TSelf FromArgb32(Argb32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgra5551(Bgra5551 source); + /// The . + static virtual TSelf FromBgra5551(Bgra5551 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgr24(Bgr24 source); + /// The . + static virtual TSelf FromBgr24(Bgr24 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgra32(Bgra32 source); + /// The . + static virtual TSelf FromBgra32(Bgra32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromAbgr32(Abgr32 source); + /// The . + static virtual TSelf FromAbgr32(Abgr32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromL8(L8 source); + /// The . + static virtual TSelf FromL8(L8 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromL16(L16 source); + /// The . + static virtual TSelf FromL16(L16 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromLa16(La16 source); + /// The . + static virtual TSelf FromLa16(La16 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromLa32(La32 source); + /// The . + static virtual TSelf FromLa32(La32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgb24(Rgb24 source); + /// The . + static virtual TSelf FromRgb24(Rgb24 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgba32(Rgba32 source); - - /// - /// Convert the pixel instance into representation. - /// - /// The reference to the destination pixel - void ToRgba32(ref Rgba32 dest); + /// The . + static virtual TSelf FromRgba32(Rgba32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgb48(Rgb48 source); + /// The . + static virtual TSelf FromRgb48(Rgb48 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgba64(Rgba64 source); + /// The . + static virtual TSelf FromRgba64(Rgba64 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); +#pragma warning restore CA1000 // Do not declare static members on generic types +} + +/// +/// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type. +/// +public interface IPixel +{ + /// + /// Convert the pixel instance into representation. + /// + /// The + virtual Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToVector4()); + + /// + /// Expands the pixel into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + Vector4 ToScaledVector4(); + + /// + /// Expands the pixel into a which is specific to the current pixel type. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + Vector4 ToVector4(); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 23dae82ab..9311c077d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -41,7 +41,7 @@ public partial struct A8 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(A8 left, A8 right) => left.Equals(right); /// @@ -52,9 +52,21 @@ public partial struct A8 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(A8 left, A8 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new() { A = this.PackedValue }; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(0, 0, 0, this.PackedValue / 255f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -66,80 +78,60 @@ public partial struct A8 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(vector.W); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(0, 0, 0, this.PackedValue / 255F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromVector4(Vector4 source) => new(Pack(source.W)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromArgb32(Argb32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgr24(Bgr24 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgra32(Bgra32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromAbgr32(Abgr32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromL8(L8 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromL16(L16 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromLa16(La16 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.A)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.PackedValue = byte.MaxValue; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromRgb24(Rgb24 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest = default; - dest.A = this.PackedValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromRgba32(Rgba32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromRgb48(Rgb48 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromRgba64(Rgba64 source) => new(ColorNumerics.From16BitTo8Bit(source.A)); /// /// Compares an object with the packed vector. @@ -153,7 +145,6 @@ public partial struct A8 : IPixel, IPackedVector /// /// The A8 packed vector to compare. /// True if the packed vectors are equal. - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(A8 other) => this.PackedValue.Equals(other.PackedValue); /// @@ -163,7 +154,6 @@ public partial struct A8 : IPixel, IPackedVector public override readonly string ToString() => $"A8({this.PackedValue})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// @@ -171,6 +161,6 @@ public partial struct A8 : IPixel, IPackedVector /// /// The float containing the value to pack. /// The containing the packed values. - [MethodImpl(InliningOptions.ShortMethod)] - private static byte Pack(float alpha) => (byte)Math.Round(Numerics.Clamp(alpha, 0, 1F) * 255F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte Pack(float alpha) => (byte)Math.Round(Numerics.Clamp(alpha, 0, 1f) * 255f); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 742f27cc0..2a29292a0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -44,12 +45,12 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// The maximum byte value. /// - private static readonly Vector4 MaxBytes = new(255); + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); /// /// The half vector value. /// - private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -57,7 +58,7 @@ public partial struct Abgr32 : IPixel, IPackedVector /// The red component. /// The green component. /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(byte r, byte g, byte b) { this.R = r; @@ -73,7 +74,7 @@ public partial struct Abgr32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(byte r, byte g, byte b, byte a) { this.R = r; @@ -89,9 +90,9 @@ public partial struct Abgr32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(float r, float g, float b, float a = 1) - : this() => this.Pack(r, g, b, a); + : this() => Pack(r, g, b, a); /// /// Initializes a new instance of the struct. @@ -99,9 +100,9 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector3 vector) - : this() => this.Pack(ref vector); + : this() => Pack(vector); /// /// Initializes a new instance of the struct. @@ -109,9 +110,9 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector4 vector) - : this() => this.Pack(ref vector); + : this() => Pack(vector); /// /// Initializes a new instance of the struct. @@ -119,29 +120,29 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// The packed value. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(uint packed) : this() => this.Abgr = packed; /// - /// Gets or sets the packed representation of the Abgrb32 struct. + /// Gets or sets the packed representation of the Abgr struct. /// public uint Abgr { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.Abgr; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this.Abgr = value; } @@ -153,7 +154,7 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Abgr32 left, Abgr32 right) => left.Equals(right); /// @@ -164,9 +165,21 @@ public partial struct Abgr32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -178,157 +191,87 @@ public partial struct Abgr32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromAbgr32(Abgr32 source) => new() { PackedValue = source.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - // We can assign the Bgr24 value directly to last three bytes of this instance. - ref byte thisRef = ref Unsafe.As(ref this); - ref byte thisRefFromB = ref Unsafe.AddByteOffset(ref thisRef, 1); - Unsafe.As(ref thisRefFromB) = source; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromL16(L16 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromLa32(La32 source) { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = byte.MaxValue; + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = byte.MaxValue + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = byte.MaxValue; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = ColorNumerics.From16BitTo8Bit(source.A) + }; /// public override readonly bool Equals(object? obj) => obj is Abgr32 abgr32 && this.Equals(abgr32); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Abgr32 other) => this.Abgr == other.Abgr; /// @@ -338,7 +281,6 @@ public partial struct Abgr32 : IPixel, IPackedVector public override readonly string ToString() => $"Abgr({this.A}, {this.B}, {this.G}, {this.R})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.Abgr.GetHashCode(); /// @@ -348,38 +290,28 @@ public partial struct Abgr32 : IPixel, IPackedVector /// The y-component /// The z-component /// The w-component - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(float x, float y, float z, float w) - { - var value = new Vector4(x, y, z, w); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Abgr32 Pack(float x, float y, float z, float w) => Pack(new Vector4(x, y, z, w)); /// /// Packs a into a uint. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector3 vector) - { - var value = new Vector4(vector, 1); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Abgr32 Pack(Vector3 vector) => Pack(new Vector4(vector, 1)); /// /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Abgr32 Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.R = (byte)vector.X; - this.G = (byte)vector.Y; - this.B = (byte)vector.Z; - this.A = (byte)vector.W; + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 7a8ee2a63..51ef76ad6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -41,15 +42,8 @@ public partial struct Argb32 : IPixel, IPackedVector /// public byte B; - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -57,7 +51,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// The red component. /// The green component. /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(byte r, byte g, byte b) { this.R = r; @@ -73,7 +67,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(byte r, byte g, byte b, byte a) { this.R = r; @@ -89,9 +83,9 @@ public partial struct Argb32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(float r, float g, float b, float a = 1) - : this() => this.Pack(r, g, b, a); + : this() => Pack(r, g, b, a); /// /// Initializes a new instance of the struct. @@ -99,9 +93,9 @@ public partial struct Argb32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector3 vector) - : this() => this.Pack(ref vector); + : this() => Pack(vector); /// /// Initializes a new instance of the struct. @@ -109,9 +103,9 @@ public partial struct Argb32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector4 vector) - : this() => this.Pack(ref vector); + : this() => Pack(vector); /// /// Initializes a new instance of the struct. @@ -119,7 +113,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// /// The packed value. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(uint packed) : this() => this.Argb = packed; @@ -128,20 +122,20 @@ public partial struct Argb32 : IPixel, IPackedVector /// public uint Argb { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.Argb; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this.Argb = value; } @@ -153,7 +147,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Argb32 left, Argb32 right) => left.Equals(right); /// @@ -164,9 +158,21 @@ public partial struct Argb32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -178,156 +184,87 @@ public partial struct Argb32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromArgb32(Argb32 source) => new() { PackedValue = source.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = source.PackedValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = byte.MaxValue; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromLa32(La32 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = byte.MaxValue + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = ColorNumerics.From16BitTo8Bit(source.A) + }; /// public override readonly bool Equals(object? obj) => obj is Argb32 argb32 && this.Equals(argb32); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Argb32 other) => this.Argb == other.Argb; /// @@ -337,7 +274,6 @@ public partial struct Argb32 : IPixel, IPackedVector public override readonly string ToString() => $"Argb({this.A}, {this.R}, {this.G}, {this.B})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.Argb.GetHashCode(); /// @@ -347,38 +283,28 @@ public partial struct Argb32 : IPixel, IPackedVector /// The y-component /// The z-component /// The w-component - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(float x, float y, float z, float w) - { - var value = new Vector4(x, y, z, w); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Argb32 Pack(float x, float y, float z, float w) => Pack(new Vector4(x, y, z, w)); /// /// Packs a into a uint. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector3 vector) - { - var value = new Vector4(vector, 1); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Argb32 Pack(Vector3 vector) => Pack(new Vector4(vector, 1f)); /// /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Argb32 Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.R = (byte)vector.X; - this.G = (byte)vector.Y; - this.B = (byte)vector.Z; - this.A = (byte)vector.W; + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index bdf7d1a7e..4756f5e19 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -14,40 +15,36 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// +/// +/// Initializes a new instance of the struct. +/// +/// The red component. +/// The green component. +/// The blue component. [StructLayout(LayoutKind.Explicit)] -public partial struct Bgr24 : IPixel +[method: MethodImpl(MethodImplOptions.AggressiveInlining)] +public partial struct Bgr24(byte r, byte g, byte b) : IPixel { /// /// The blue component. /// [FieldOffset(0)] - public byte B; + public byte B = b; /// /// The green component. /// [FieldOffset(1)] - public byte G; + public byte G = g; /// /// The red component. /// [FieldOffset(2)] - public byte R; + public byte R = r; - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] - public Bgr24(byte r, byte g, byte b) - { - this.R = r; - this.G = g; - this.B = b; - } + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Compares two objects for equality. @@ -57,7 +54,7 @@ public partial struct Bgr24 : IPixel /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgr24 left, Bgr24 right) => left.Equals(right); /// @@ -68,9 +65,21 @@ public partial struct Bgr24 : IPixel /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, byte.MaxValue) / MaxBytes; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -82,143 +91,90 @@ public partial struct Bgr24 : IPixel public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromVector4(Vector4 source) { - Rgba32 rgba = default; - rgba.FromVector4(vector); - this.FromRgba32(rgba); - } + source *= MaxBytes; + source += Half; + source = Numerics.Clamp(source, Vector4.Zero, MaxBytes); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; + Vector128 result = Vector128.ConvertToInt32(source.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromArgb32(Argb32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromLa16(La16 source) => new(source.L, source.L, source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromLa32(La32 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - // We can assign this instances value directly to last three bytes of the Abgr32. - ref byte sourceRef = ref Unsafe.As(ref source); - ref byte sourceRefFromB = ref Unsafe.AddByteOffset(ref sourceRef, 1); - this = Unsafe.As(ref sourceRefFromB); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source.Bgr; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B) + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B) + }; /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); /// @@ -228,6 +184,5 @@ public partial struct Bgr24 : IPixel public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => HashCode.Combine(this.R, this.B, this.G); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 0874eb825..4ea149dda 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -13,7 +13,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct Bgr565 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// +/// The vector containing the components for the packed value. +/// +public partial struct Bgr565(Vector3 vector) : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -26,16 +32,8 @@ public partial struct Bgr565 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed value. - /// - public Bgr565(Vector3 vector) => this.PackedValue = Pack(ref vector); - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -45,7 +43,7 @@ public partial struct Bgr565 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgr565 left, Bgr565 right) => left.Equals(right); /// @@ -56,9 +54,17 @@ public partial struct Bgr565 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector3(), 1F); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -70,87 +76,19 @@ public partial struct Bgr565 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector3(), 1F); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromVector4(source.ToVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromVector4(source.ToVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromVector4(source.ToVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromVector4(source.ToVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromVector4(source.ToVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector3(source.X, source.Y, source.Z)) }; /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector3 ToVector3() => new( ((this.PackedValue >> 11) & 0x1F) * (1F / 31F), ((this.PackedValue >> 5) & 0x3F) * (1F / 63F), @@ -160,22 +98,20 @@ public partial struct Bgr565 : IPixel, IPackedVector public override readonly bool Equals(object? obj) => obj is Bgr565 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Bgr565 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector3(); + Vector3 vector = this.ToVector3(); return FormattableString.Invariant($"Bgr565({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - private static ushort Pack(ref Vector3 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(Vector3 vector) { vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index f50846357..aeffd5760 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -38,15 +39,8 @@ public partial struct Bgra32 : IPixel, IPackedVector /// public byte A; - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -54,7 +48,7 @@ public partial struct Bgra32 : IPixel, IPackedVector /// The red component. /// The green component. /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgra32(byte r, byte g, byte b) { this.R = r; @@ -70,7 +64,7 @@ public partial struct Bgra32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgra32(byte r, byte g, byte b, byte a) { this.R = r; @@ -84,10 +78,10 @@ public partial struct Bgra32 : IPixel, IPackedVector /// public uint Bgra { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } @@ -106,7 +100,7 @@ public partial struct Bgra32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra32 left, Bgra32 right) => left.Equals(right); /// @@ -117,9 +111,21 @@ public partial struct Bgra32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -131,150 +137,82 @@ public partial struct Bgra32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromBgra32(Bgra32 source) => new() { PackedValue = source.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = byte.MaxValue; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromLa32(La32 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = byte.MaxValue + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = ColorNumerics.From16BitTo8Bit(source.A) + }; /// public override readonly bool Equals(object? obj) => obj is Bgra32 other && this.Equals(other); @@ -292,16 +230,14 @@ public partial struct Bgra32 : IPixel, IPackedVector /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Bgra32 Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.R = (byte)vector.X; - this.G = (byte)vector.Y; - this.B = (byte)vector.Z; - this.A = (byte)vector.W; + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 4bb3f8a0e..baaa49ce1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -public partial struct Bgra4444 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the components for the packed vector. +public partial struct Bgra4444(Vector4 vector) : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -26,14 +30,8 @@ public partial struct Bgra4444 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the components for the packed vector. - public Bgra4444(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -43,7 +41,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra4444 left, Bgra4444 right) => left.Equals(right); /// @@ -54,120 +52,62 @@ public partial struct Bgra4444 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 4, 4, 4, 4), - PixelColorType.BGR | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { - const float Max = 1 / 15F; + const float max = 1 / 15f; return new Vector4( (this.PackedValue >> 8) & 0x0F, (this.PackedValue >> 4) & 0x0F, this.PackedValue & 0x0F, - (this.PackedValue >> 12) & 0x0F) * Max; + (this.PackedValue >> 12) & 0x0F) * max; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 4, 4, 4, 4), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// public override readonly bool Equals(object? obj) => obj is Bgra4444 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Bgra4444 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"Bgra4444({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - private static ushort Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); return (ushort)((((int)Math.Round(vector.W * 15F) & 0x0F) << 12) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index d57545dee..381f4628f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -13,7 +13,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -public partial struct Bgra5551 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// +/// The vector containing the components for the packed vector. +/// +public partial struct Bgra5551(Vector4 vector) : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -27,16 +33,8 @@ public partial struct Bgra5551 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Bgra5551(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -46,7 +44,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra5551 left, Bgra5551 right) => left.Equals(right); /// @@ -57,33 +55,15 @@ public partial struct Bgra5551 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 5, 5, 5, 1), - PixelColorType.BGR | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new( ((this.PackedValue >> 10) & 0x1F) / 31F, ((this.PackedValue >> 5) & 0x1F) / 31F, @@ -91,81 +71,45 @@ public partial struct Bgra5551 : IPixel, IPackedVector (this.PackedValue >> 15) & 0x01); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this = source; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 5, 5, 5, 1), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgra5551(Bgra5551 source) => new() { PackedValue = source.PackedValue }; /// - public override bool Equals(object? obj) => obj is Bgra5551 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Bgra5551 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Bgra5551 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"Bgra5551({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - private static ushort Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); return (ushort)( diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index d8f1dd0ac..fba320672 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -14,13 +15,18 @@ namespace SixLabors.ImageSharp.PixelFormats; /// public partial struct Byte4 : IPixel, IPackedVector { + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + /// /// Initializes a new instance of the struct. /// /// /// A vector containing the initial values for the components of the Byte4 structure. /// - public Byte4(Vector4 vector) => this.PackedValue = Pack(ref vector); + public Byte4(Vector4 vector) => this.PackedValue = Pack(vector); /// /// Initializes a new instance of the struct. @@ -31,8 +37,8 @@ public partial struct Byte4 : IPixel, IPackedVector /// The w-component public Byte4(float x, float y, float z, float w) { - var vector = new Vector4(x, y, z, w); - this.PackedValue = Pack(ref vector); + Vector4 vector = new(x, y, z, w); + this.PackedValue = Pack(vector); } /// @@ -46,7 +52,7 @@ public partial struct Byte4 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Byte4 left, Byte4 right) => left.Equals(right); /// @@ -57,110 +63,60 @@ public partial struct Byte4 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 8, 8, 8, 8), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector * 255F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() => new() { PackedValue = this.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4() / 255F; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4() / 255f; /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new( this.PackedValue & 0xFF, - (this.PackedValue >> 0x8) & 0xFF, - (this.PackedValue >> 0x10) & 0xFF, - (this.PackedValue >> 0x18) & 0xFF); + (this.PackedValue >> 8) & 0xFF, + (this.PackedValue >> 16) & 0xFF, + (this.PackedValue >> 24) & 0xFF); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromScaledVector4(Vector4 source) => FromVector4(source * 255f); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromRgba32(Rgba32 source) => new() { PackedValue = source.PackedValue }; /// public override readonly bool Equals(object? obj) => obj is Byte4 byte4 && this.Equals(byte4); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Byte4 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"Byte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } @@ -169,18 +125,17 @@ public partial struct Byte4 : IPixel, IPackedVector /// /// The vector containing the values to pack. /// The containing the packed values. - [MethodImpl(InliningOptions.ShortMethod)] - private static uint Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(Vector4 vector) { - const float Max = 255F; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - // Clamp the value between min and max values - vector = Numerics.Clamp(vector, Vector4.Zero, new Vector4(Max)); + Vector128 result = Vector128.ConvertToUInt32(vector.AsVector128()); - uint byte4 = (uint)Math.Round(vector.X) & 0xFF; - uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 0x8; - uint byte2 = ((uint)Math.Round(vector.Z) & 0xFF) << 0x10; - uint byte1 = ((uint)Math.Round(vector.W) & 0xFF) << 0x18; + uint byte4 = result.GetElement(0) & 0xFF; + uint byte3 = result.GetElement(1) << 8; + uint byte2 = result.GetElement(2) << 16; + uint byte1 = result.GetElement(3) << 24; return byte4 | byte3 | byte2 | byte1; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 01ae9fc5f..2d8ab5ff0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -12,16 +12,14 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form. /// /// -public partial struct HalfSingle : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The single component value. +public partial struct HalfSingle(float value) : IPixel, IPackedVector { - /// - /// Initializes a new instance of the struct. - /// - /// The single component value. - public HalfSingle(float value) => this.PackedValue = HalfTypeHelper.Pack(value); - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = HalfTypeHelper.Pack(value); /// /// Compares two objects for equality. @@ -31,7 +29,7 @@ public partial struct HalfSingle : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfSingle left, HalfSingle right) => left.Equals(right); /// @@ -42,31 +40,11 @@ public partial struct HalfSingle : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(1, 16), - PixelColorType.Red, - PixelAlphaRepresentation.None); - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - float scaled = vector.X; - scaled *= 2F; - scaled--; - this.PackedValue = HalfTypeHelper.Pack(scaled); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { float single = this.ToSingle() + 1F; @@ -75,87 +53,49 @@ public partial struct HalfSingle : IPixel, IPackedVector } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = HalfTypeHelper.Pack(vector.X); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new(this.ToSingle(), 0, 0, 1F); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 16), + PixelColorType.Red, + PixelAlphaRepresentation.None); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromScaledVector4(Vector4 source) + { + float scaled = source.X; + scaled *= 2F; + scaled--; + return new() { PackedValue = HalfTypeHelper.Pack(scaled) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromVector4(Vector4 source) => new() { PackedValue = HalfTypeHelper.Pack(source.X) }; /// /// Expands the packed representation into a . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue); /// public override readonly bool Equals(object? obj) => obj is HalfSingle other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(HalfSingle other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() => FormattableString.Invariant($"HalfSingle({this.ToSingle():#0.##})"); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index d591dd855..525638b1d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -38,7 +38,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector2 left, HalfVector2 right) => left.Equals(right); /// @@ -49,111 +49,55 @@ public partial struct HalfVector2 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(2, 16, 16), - PixelColorType.Red | PixelColorType.Green, - PixelAlphaRepresentation.None); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled.X, scaled.Y); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { - var scaled = this.ToVector2(); + Vector2 scaled = this.ToVector2(); scaled += Vector2.One; scaled /= 2F; - return new Vector4(scaled, 0F, 1F); + return new(scaled, 0F, 1F); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(vector.X, vector.Y); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { - var vector = this.ToVector2(); - return new Vector4(vector.X, vector.Y, 0F, 1F); + Vector2 vector = this.ToVector2(); + return new(vector.X, vector.Y, 0F, 1F); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromScaledVector4(Vector4 source) + { + Vector2 scaled = new Vector2(source.X, source.Y) * 2F; + scaled -= Vector2.One; + return new() { PackedValue = Pack(scaled.X, scaled.Y) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromVector4(Vector4 source) => new() { PackedValue = Pack(source.X, source.Y) }; /// /// Expands the packed representation into a . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { Vector2 vector; @@ -166,21 +110,19 @@ public partial struct HalfVector2 : IPixel, IPackedVector public override readonly bool Equals(object? obj) => obj is HalfVector2 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(HalfVector2 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector2(); + Vector2 vector = this.ToVector2(); return FormattableString.Invariant($"HalfVector2({vector.X:#0.##}, {vector.Y:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { uint num2 = HalfTypeHelper.Pack(x); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index ca6bff230..28f3849eb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct HalfVector4 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// A vector containing the initial values for the components +public partial struct HalfVector4(Vector4 vector) : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -26,14 +30,8 @@ public partial struct HalfVector4 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// A vector containing the initial values for the components - public HalfVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public ulong PackedValue { get; set; } + public ulong PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -43,7 +41,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector4 left, HalfVector4 right) => left.Equals(right); /// @@ -54,44 +52,21 @@ public partial struct HalfVector4 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 16, 16, 16, 16), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.FromVector4(vector); - } - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { - var scaled = this.ToVector4(); + Vector4 scaled = this.ToVector4(); scaled += Vector4.One; scaled /= 2F; return scaled; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new( HalfTypeHelper.Unpack((ushort)this.PackedValue), HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), @@ -99,77 +74,42 @@ public partial struct HalfVector4 : IPixel, IPackedVector HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromScaledVector4(Vector4 source) + { + source *= 2F; + source -= Vector4.One; + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// public override readonly bool Equals(object? obj) => obj is HalfVector4 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(HalfVector4 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"HalfVector4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// @@ -177,8 +117,8 @@ public partial struct HalfVector4 : IPixel, IPackedVector /// /// The vector containing the values to pack. /// The containing the packed values. - [MethodImpl(InliningOptions.ShortMethod)] - private static ulong Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Pack(Vector4 vector) { ulong num4 = HalfTypeHelper.Pack(vector.X); ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 8522da3fb..52bb85fe2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -12,18 +12,16 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct L16 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The luminance component +public partial struct L16(ushort luminance) : IPixel, IPackedVector { private const float Max = ushort.MaxValue; - /// - /// Initializes a new instance of the struct. - /// - /// The luminance component - public L16(ushort luminance) => this.PackedValue = luminance; - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = luminance; /// /// Compares two objects for equality. @@ -33,7 +31,7 @@ public partial struct L16 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(L16 left, L16 right) => left.Equals(right); /// @@ -44,9 +42,29 @@ public partial struct L16 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(L16 left, L16 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = ColorNumerics.From16BitTo8Bit(this.PackedValue); + return new(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + float scaled = this.PackedValue / Max; + return new Vector4(scaled, scaled, scaled, 1f); + } + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -58,127 +76,77 @@ public partial struct L16 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() - { - float scaled = this.PackedValue / Max; - return new Vector4(scaled, scaled, scaled, 1F); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromArgb32(Argb32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.PackedValue = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromL8(L8 source) => new(ColorNumerics.From8BitTo16Bit(source.PackedValue)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromL16(L16 source) => new(source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.PackedValue = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromLa16(La16 source) => new(ColorNumerics.From8BitTo16Bit(source.L)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.PackedValue = source.L; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromLa32(La32 source) => new(source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromRgb24(Rgb24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(this.PackedValue); - dest.R = rgb; - dest.G = rgb; - dest.B = rgb; - dest.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromRgba32(Rgba32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromRgb48(Rgb48 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromRgba64(Rgba64 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(L16 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() => $"L16({this.PackedValue})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.PackedValue = ColorNumerics.Get16BitBT709Luminance( - vector.X, - vector.Y, - vector.Z); + return ColorNumerics.Get16BitBT709Luminance(vector.X, vector.Y, vector.Z); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 706fc1101..7c47ab10c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -12,19 +13,24 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct L8 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The luminance component. +public partial struct L8(byte luminance) : IPixel, IPackedVector { - private static readonly Vector4 MaxBytes = new(255F); - private static readonly Vector4 Half = new(0.5F); + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); /// - /// Initializes a new instance of the struct. + /// The half vector value. /// - /// The luminance component. - public L8(byte luminance) => this.PackedValue = luminance; + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// - public byte PackedValue { get; set; } + public byte PackedValue { get; set; } = luminance; /// /// Compares two objects for equality. @@ -34,7 +40,7 @@ public partial struct L8 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(L8 left, L8 right) => left.Equals(right); /// @@ -45,9 +51,29 @@ public partial struct L8 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(L8 left, L8 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = this.PackedValue; + return new(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + float rgb = this.PackedValue / 255f; + return new Vector4(rgb, rgb, rgb, 1f); + } + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -59,115 +85,81 @@ public partial struct L8 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() - { - float rgb = this.PackedValue / 255F; - return new Vector4(rgb, rgb, rgb, 1F); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromL8(L8 source) => new(source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromL16(L16 source) => new(ColorNumerics.From16BitTo8Bit(source.PackedValue)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromLa16(La16 source) => new(source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.PackedValue = source.L; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.L)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromRgb24(Rgb24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.PackedValue; - dest.G = this.PackedValue; - dest.B = this.PackedValue; - dest.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromRgba32(Rgba32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - => this.PackedValue = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromRgb48(Rgb48 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - => this.PackedValue = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromRgba64(Rgba64 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// public override readonly bool Equals(object? obj) => obj is L8 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(L8 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() => $"L8({this.PackedValue})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.PackedValue = ColorNumerics.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return ColorNumerics.Get8BitBT709Luminance(result.GetElement(0), result.GetElement(4), result.GetElement(8)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index e30673153..558f15332 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -13,34 +14,35 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// +/// +/// Initializes a new instance of the struct. +/// +/// The luminance component. +/// The alpha component. [StructLayout(LayoutKind.Explicit)] -public partial struct La16 : IPixel, IPackedVector +public partial struct La16(byte l, byte a) : IPixel, IPackedVector { - private static readonly Vector4 MaxBytes = new(255F); - private static readonly Vector4 Half = new(0.5F); + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + + /// + /// The half vector value. + /// + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Gets or sets the luminance component. /// [FieldOffset(0)] - public byte L; + public byte L = l; /// /// Gets or sets the alpha component. /// [FieldOffset(1)] - public byte A; - - /// - /// Initializes a new instance of the struct. - /// - /// The luminance component. - /// The alpha component. - public La16(byte l, byte a) - { - this.L = l; - this.A = a; - } + public byte A = a; /// public ushort PackedValue @@ -57,7 +59,7 @@ public partial struct La16 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(La16 left, La16 right) => left.Equals(right); /// @@ -68,9 +70,26 @@ public partial struct La16 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(La16 left, La16 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.L, this.L, this.L, this.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + const float max = 255f; + float rgb = this.L / max; + return new Vector4(rgb, rgb, rgb, this.A / max); + } + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -82,160 +101,83 @@ public partial struct La16 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - 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 string ToString() => $"La16({this.L}, {this.A})"; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromScaledVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromL16(L16 source) => new(ColorNumerics.From16BitTo8Bit(source.PackedValue), byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.L = source.PackedValue; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromL8(L8 source) => new(source.PackedValue, byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromLa16(La16 source) => new(source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) - { - this.L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.L), ColorNumerics.From16BitTo8Bit(source.A)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromRgb24(Rgb24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); - - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromRgba32(Rgba32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromRgb48(Rgb48 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); /// - public void FromRgba64(Rgba64 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); - - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + public static La16 FromRgba64(Rgba64 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), ColorNumerics.From16BitTo8Bit(source.A)); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + /// + public override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.L; - dest.G = this.L; - dest.B = this.L; - dest.A = this.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + public override readonly string ToString() => $"La16({this.L}, {this.A})"; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() - { - const float Max = 255F; - float rgb = this.L / Max; - return new Vector4(rgb, rgb, rgb, this.A / Max); - } + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static La16 Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.L = ColorNumerics.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); - this.A = (byte)vector.W; + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + byte l = ColorNumerics.Get8BitBT709Luminance(result.GetElement(0), result.GetElement(4), result.GetElement(8)); + byte a = result.GetElement(12); + + return new(l, a); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 867bfacd3..c0d024356 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -13,8 +13,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// +/// +/// Initializes a new instance of the struct. +/// +/// The luminance component. +/// The alpha component. [StructLayout(LayoutKind.Explicit)] -public partial struct La32 : IPixel, IPackedVector +public partial struct La32(ushort l, ushort a) : IPixel, IPackedVector { private const float Max = ushort.MaxValue; @@ -22,32 +27,21 @@ public partial struct La32 : IPixel, IPackedVector /// Gets or sets the luminance component. /// [FieldOffset(0)] - public ushort L; + public ushort L = l; /// /// Gets or sets the alpha component. /// [FieldOffset(2)] - public ushort A; - - /// - /// Initializes a new instance of the struct. - /// - /// The luminance component. - /// The alpha component. - public La32(ushort l, ushort a) - { - this.L = l; - this.A = a; - } + public ushort A = a; /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } @@ -59,7 +53,7 @@ public partial struct La32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(La32 left, La32 right) => left.Equals(right); /// @@ -70,9 +64,29 @@ public partial struct La32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(La32 left, La32 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = ColorNumerics.From16BitTo8Bit(this.L); + return new(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(this.A)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + float rgb = this.L / Max; + return new Vector4(rgb, rgb, rgb, this.A / Max); + } + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -84,179 +98,108 @@ public partial struct La32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - 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 string ToString() => $"La32({this.L}, {this.A})"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromScaledVector4(Vector4 source) => Pack(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromArgb32(Argb32 source) { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgra32(Bgra32 source) { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromAbgr32(Abgr32 source) { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.L = source.PackedValue; - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromL16(L16 source) => new(source.PackedValue, ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromL8(L8 source) => new(ColorNumerics.From8BitTo16Bit(source.PackedValue), ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromLa16(La16 source) { - this.L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.From8BitTo16Bit(source.L); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this = source; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromLa32(La32 source) => new(source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgb24(Rgb24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgb48(Rgb48 source) { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + return new(l, ushort.MaxValue); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgba32(Rgba32 source) { - this.L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgba64(Rgba64 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), source.A); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + /// + public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(this.L); - dest.R = rgb; - dest.G = rgb; - dest.B = rgb; - dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); - } + public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + /// + public override readonly string ToString() => $"La32({this.L}, {this.A})"; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() - { - float scaled = this.L / Max; - return new Vector4(scaled, scaled, scaled, this.A / Max); - } + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static La32 Pack(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.L = ColorNumerics.Get16BitBT709Luminance( - vector.X, - vector.Y, - vector.Z); - - this.A = (ushort)MathF.Round(vector.W); + ushort l = ColorNumerics.Get16BitBT709Luminance(vector.X, vector.Y, vector.Z); + ushort a = (ushort)MathF.Round(vector.W); + return new(l, a); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index c5eb2c2de..138490b9c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -12,12 +12,15 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -public partial struct NormalizedByte2 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct NormalizedByte2(Vector2 vector) : IPixel, IPackedVector { - private const float MaxPos = 127F; - + private const float MaxPos = 127f; private static readonly Vector2 Half = new(MaxPos); - private static readonly Vector2 MinusOne = new(-1F); + private static readonly Vector2 MinusOne = new(-1f); /// /// Initializes a new instance of the struct. @@ -29,14 +32,8 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedByte2(Vector2 vector) => this.PackedValue = Pack(vector); - /// - public ushort PackedValue { get; set; } + public ushort PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -46,7 +43,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right) => left.Equals(right); /// @@ -57,9 +54,23 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() + { + Vector2 scaled = this.ToVector2(); + scaled += Vector2.One; + scaled /= 2f; + return new Vector4(scaled, 0f, 1f); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -71,98 +82,24 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromScaledVector4(Vector4 source) { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; + Vector2 scaled = new Vector2(source.X, source.Y) * 2f; scaled -= Vector2.One; - this.PackedValue = Pack(scaled); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); + return new() { PackedValue = Pack(scaled) }; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() => new( (sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos, (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos); @@ -171,21 +108,19 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector obj is NormalizedByte2 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(NormalizedByte2 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector2(); + Vector2 vector = this.ToVector2(); return FormattableString.Invariant($"NormalizedByte2({vector.X:#0.##}, {vector.Y:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(Vector2 vector) { vector = Vector2.Clamp(vector, MinusOne, Vector2.One) * Half; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 6cf92688e..d9a3cc1cd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -12,12 +13,15 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct NormalizedByte4 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct NormalizedByte4(Vector4 vector) : IPixel, IPackedVector { - private const float MaxPos = 127F; - - private static readonly Vector4 Half = new(MaxPos); - private static readonly Vector4 MinusOne = new(-1F); + private const float MaxPos = 127f; + private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); + private static readonly Vector4 MinusOne = Vector128.Create(-1f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -31,14 +35,8 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedByte4(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public uint PackedValue { get; set; } + public uint PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -48,7 +46,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right) => left.Equals(right); /// @@ -59,44 +57,21 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 8, 8, 8, 8), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.FromVector4(vector); - } - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { - var scaled = this.ToVector4(); + Vector4 scaled = this.ToVector4(); scaled += Vector4.One; - scaled /= 2F; + scaled /= 2f; return scaled; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new( (sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos, (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos, @@ -104,81 +79,46 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector> 24) & 0xFF) / MaxPos); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromScaledVector4(Vector4 source) + { + source *= 2f; + source -= Vector4.One; + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// public override readonly bool Equals(object? obj) => obj is NormalizedByte4 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(NormalizedByte4 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"NormalizedByte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] - private static uint Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(Vector4 vector) { vector = Numerics.Clamp(vector, MinusOne, Vector4.One) * Half; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 03fa2737e..8971e9879 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -public partial struct NormalizedShort2 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct NormalizedShort2(Vector2 vector) : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -30,14 +34,8 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedShort2(Vector2 vector) => this.PackedValue = Pack(vector); - /// - public uint PackedValue { get; set; } + public uint PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -47,7 +45,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right) => left.Equals(right); /// @@ -58,9 +56,23 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() + { + Vector2 scaled = this.ToVector2(); + scaled += Vector2.One; + scaled /= 2f; + return new Vector4(scaled, 0f, 1f); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -72,98 +84,24 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromScaledVector4(Vector4 source) { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; + Vector2 scaled = new Vector2(source.X, source.Y) * 2f; scaled -= Vector2.One; - this.PackedValue = Pack(scaled); + return new() { PackedValue = Pack(scaled) }; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.ToVector2(), 0, 1); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() => new( (short)(this.PackedValue & 0xFFFF) / MaxPos, (short)(this.PackedValue >> 0x10) / MaxPos); @@ -172,21 +110,19 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector public override readonly bool Equals(object? obj) => obj is NormalizedShort2 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(NormalizedShort2 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector2(); + Vector2 vector = this.ToVector2(); return FormattableString.Invariant($"NormalizedShort2({vector.X:#0.##}, {vector.Y:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector *= Max; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 89ba2a23b..727198076 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -12,12 +13,15 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -public partial struct NormalizedShort4 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct NormalizedShort4(Vector4 vector) : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; - - private static readonly Vector4 Max = new(MaxPos); + private static readonly Vector4 Max = Vector128.Create(MaxPos).AsVector4(); private static readonly Vector4 Min = Vector4.Negate(Max); /// @@ -32,14 +36,8 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedShort4(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public ulong PackedValue { get; set; } + public ulong PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -49,7 +47,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right) => left.Equals(right); /// @@ -60,44 +58,21 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 16, 16, 16, 16), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.FromVector4(vector); - } - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { - var scaled = this.ToVector4(); + Vector4 scaled = this.ToVector4(); scaled += Vector4.One; - scaled /= 2F; + scaled /= 2f; return scaled; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new( (short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxPos, (short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxPos, @@ -105,81 +80,46 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector (short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxPos); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromScaledVector4(Vector4 source) + { + source *= 2f; + source -= Vector4.One; + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// public override readonly bool Equals(object? obj) => obj is NormalizedShort4 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(NormalizedShort4 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"NormalizedShort4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] - private static ulong Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Pack(Vector4 vector) { vector *= Max; vector = Numerics.Clamp(vector, Min, Max); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index c18c3d868..128fd8daf 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs @@ -71,7 +71,7 @@ public partial struct RgbaVector ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); - dp.ConvertFromRgbaScaledVector4(sp); + dp.Pack(sp); } } @@ -90,7 +90,7 @@ public partial struct RgbaVector ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); - dp.ConvertFromRgbaScaledVector4(sp); + dp.Pack(sp); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 8f2e19840..c31bfc613 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -public partial struct Rg32 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct Rg32(Vector2 vector) : IPixel, IPackedVector { private static readonly Vector2 Max = new(ushort.MaxValue); @@ -26,14 +30,8 @@ public partial struct Rg32 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rg32(Vector2 vector) => this.PackedValue = Pack(vector); - /// - public uint PackedValue { get; set; } + public uint PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -43,7 +41,7 @@ public partial struct Rg32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rg32 left, Rg32 right) => left.Equals(right); /// @@ -54,9 +52,26 @@ public partial struct Rg32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + => new( + ColorNumerics.From16BitTo8Bit((ushort)(this.PackedValue & 0xFFFF)), + ColorNumerics.From16BitTo8Bit((ushort)(this.PackedValue >> 16)), + byte.MinValue, + byte.MaxValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -68,108 +83,86 @@ public partial struct Rg32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromArgb32(Argb32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromBgr24(Bgr24 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromBgra32(Bgra32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromAbgr32(Abgr32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromL8(L8 source) => new(ColorNumerics.From8BitTo16Bit(source.PackedValue), ColorNumerics.From8BitTo16Bit(source.PackedValue)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromL16(L16 source) => new(source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromLa16(La16 source) => new(ColorNumerics.From8BitTo16Bit(source.L), ColorNumerics.From8BitTo16Bit(source.L)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromLa32(La32 source) => new(source.L, source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromRgb24(Rgb24 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromRgba32(Rgba32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromRgb48(Rgb48 source) => new(source.R, source.G); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromRgba64(Rgba64 source) => new(source.R, source.G); /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rg32 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() { - var vector = this.ToVector2(); + Vector2 vector = this.ToVector2(); return FormattableString.Invariant($"Rg32({vector.X:#0.##}, {vector.Y:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 008de06bf..fc54accfd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -14,43 +15,36 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// +/// +/// Initializes a new instance of the struct. +/// +/// The red component. +/// The green component. +/// The blue component. [StructLayout(LayoutKind.Explicit)] -public partial struct Rgb24 : IPixel +[method: MethodImpl(MethodImplOptions.AggressiveInlining)] +public partial struct Rgb24(byte r, byte g, byte b) : IPixel { /// /// The red component. /// [FieldOffset(0)] - public byte R; + public byte R = r; /// /// The green component. /// [FieldOffset(1)] - public byte G; + public byte G = g; /// /// The blue component. /// [FieldOffset(2)] - public byte B; + public byte B = b; - private static readonly Vector4 MaxBytes = new(byte.MaxValue); - private static readonly Vector4 Half = new(0.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb24(byte r, byte g, byte b) - { - this.R = r; - this.G = g; - this.B = b; - } + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Allows the implicit conversion of an instance of to a @@ -58,15 +52,8 @@ public partial struct Rgb24 : IPixel /// /// The instance of to convert. /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb24(ColorSpaces.Rgb color) - { - var vector = new Vector4(color.ToVector3(), 1F); - - Rgb24 rgb = default; - rgb.FromScaledVector4(vector); - return rgb; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Rgb24(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f)); /// /// Compares two objects for equality. @@ -76,7 +63,7 @@ public partial struct Rgb24 : IPixel /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgb24 left, Rgb24 right) => left.Equals(right); /// @@ -87,9 +74,17 @@ public partial struct Rgb24 : IPixel /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -100,163 +95,103 @@ public partial struct Rgb24 : IPixel /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromVector4(Vector4 source) + { + source *= MaxBytes; + source += Half; + source = Numerics.Clamp(source, Vector4.Zero, MaxBytes); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); + Vector128 result = Vector128.ConvertToInt32(source.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8)); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromArgb32(Argb32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromLa16(La16 source) => new(source.L, source.L, source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromLa32(La32 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source.Rgb; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B) + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B) + }; /// public override readonly bool Equals(object? obj) => obj is Rgb24 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rgb24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => HashCode.Combine(this.R, this.B, this.G); /// public override readonly string ToString() => $"Rgb24({this.R}, {this.G}, {this.B})"; - - /// - /// Packs a into a color. - /// - /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector4 vector) - { - vector *= MaxBytes; - vector += Half; - vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - - this.R = (byte)vector.X; - this.G = (byte)vector.Y; - this.B = (byte)vector.Z; - } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index ae4d6dc01..af901e3d2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -55,7 +55,7 @@ public partial struct Rgb48 : IPixel /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgb48 left, Rgb48 right) => left.Equals(right); /// @@ -66,9 +66,22 @@ public partial struct Rgb48 : IPixel /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + => new(ColorNumerics.From16BitTo8Bit(this.R), ColorNumerics.From16BitTo8Bit(this.G), ColorNumerics.From16BitTo8Bit(this.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.R / Max, this.G / Max, this.B / Max, 1f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -80,152 +93,88 @@ public partial struct Rgb48 : IPixel public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromVector4(Vector4 source) { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.R = (ushort)MathF.Round(vector.X); - this.G = (ushort)MathF.Round(vector.Y); - this.B = (ushort)MathF.Round(vector.Z); + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One) * Max; + return new((ushort)MathF.Round(source.X), (ushort)MathF.Round(source.Y), (ushort)MathF.Round(source.Z)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.R / Max, this.G / Max, this.B / Max, 1F); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromArgb32(Argb32 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromBgr24(Bgr24 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromBgra32(Bgra32 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this = source.Rgb; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromRgba64(Rgba64 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromL8(L8 source) { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; + ushort rgb = ColorNumerics.From8BitTo16Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromL16(L16 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromLa16(La16 source) { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; + ushort rgb = ColorNumerics.From8BitTo16Bit(source.L); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromLa32(La32 source) => new(source.L, source.L, source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromRgb24(Rgb24 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromRgba32(Rgba32 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - dest.G = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - dest.B = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - dest.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromAbgr32(Abgr32 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B); /// public override readonly bool Equals(object? obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rgb48 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); /// public override readonly string ToString() => $"Rgb48({this.R}, {this.G}, {this.B})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index ac1eedb07..85ae045a0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -13,7 +13,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -public partial struct Rgba1010102 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct Rgba1010102(Vector4 vector) : IPixel, IPackedVector { private static readonly Vector4 Multiplier = new(1023F, 1023F, 1023F, 3F); @@ -29,14 +33,8 @@ public partial struct Rgba1010102 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rgba1010102(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public uint PackedValue { get; set; } + public uint PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -46,7 +44,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba1010102 left, Rgba1010102 right) => left.Equals(right); /// @@ -57,33 +55,15 @@ public partial struct Rgba1010102 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 10, 10, 10, 2), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() => new Vector4( (this.PackedValue >> 0) & 0x03FF, (this.PackedValue >> 10) & 0x03FF, @@ -91,81 +71,41 @@ public partial struct Rgba1010102 : IPixel, IPackedVector (this.PackedValue >> 30) & 0x03) / Multiplier; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 10, 10, 10, 2), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// public override readonly bool Equals(object? obj) => obj is Rgba1010102 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rgba1010102 other) => this.PackedValue == other.PackedValue; /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"Rgba1010102({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - [MethodImpl(InliningOptions.ShortMethod)] - private static uint Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Multiplier; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 8b2a078db..83c376c9f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; @@ -43,8 +44,8 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public byte A; - private static readonly Vector4 MaxBytes = new(byte.MaxValue); - private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -52,7 +53,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The red component. /// The green component. /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(byte r, byte g, byte b) { this.R = r; @@ -68,7 +69,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(byte r, byte g, byte b, byte a) { this.R = r; @@ -84,9 +85,9 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(float r, float g, float b, float a = 1) - : this() => this.Pack(r, g, b, a); + : this() => Pack(r, g, b, a); /// /// Initializes a new instance of the struct. @@ -94,9 +95,9 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(Vector3 vector) - : this() => this.Pack(ref vector); + : this() => Pack(vector); /// /// Initializes a new instance of the struct. @@ -104,9 +105,9 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// The vector containing the components for the packed vector. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(Vector4 vector) - : this() => this = PackNew(ref vector); + : this() => this = Pack(vector); /// /// Initializes a new instance of the struct. @@ -114,7 +115,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// The packed value. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(uint packed) : this() => this.Rgba = packed; @@ -123,10 +124,10 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public uint Rgba { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } @@ -135,10 +136,10 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public Rgb24 Rgb { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => new(this.R, this.G, this.B); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.R = value.R; @@ -152,10 +153,10 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public Bgr24 Bgr { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => new(this.R, this.G, this.B); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.R = value.R; @@ -167,10 +168,10 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.Rgba; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this.Rgba = value; } @@ -180,15 +181,8 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// The instance of to convert. /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgba32(ColorSpaces.Rgb color) - { - var vector = new Vector4(color.ToVector3(), 1F); - - Rgba32 rgba = default; - rgba.FromScaledVector4(vector); - return rgba; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Rgba32(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F)); /// /// Compares two objects for equality. @@ -198,7 +192,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba32 left, Rgba32 right) => left.Equals(right); /// @@ -209,7 +203,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba32 left, Rgba32 right) => !left.Equals(right); /// @@ -224,7 +218,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The . /// /// Hexadecimal string is not in the correct format. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgba32 ParseHex(string hex) { Guard.NotNull(hex, nameof(hex)); @@ -249,7 +243,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParseHex(string? hex, out Rgba32 result) { result = default; @@ -270,6 +264,18 @@ public partial struct Rgba32 : IPixel, IPackedVector return true; } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => this; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -281,140 +287,82 @@ public partial struct Rgba32 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.Bgr = source; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = byte.MaxValue; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - this.A = source.A; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromLa32(La32 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.Rgb = source; - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest = this; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgba32(Rgba32 source) => new() { PackedValue = source.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = byte.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgb48(Rgb48 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = byte.MaxValue + }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) - { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgba64(Rgba64 source) + => new() + { + R = ColorNumerics.From16BitTo8Bit(source.R), + G = ColorNumerics.From16BitTo8Bit(source.G), + B = ColorNumerics.From16BitTo8Bit(source.B), + A = ColorNumerics.From16BitTo8Bit(source.A) + }; /// /// Converts the value of this instance to a hexadecimal string. @@ -430,31 +378,14 @@ public partial struct Rgba32 : IPixel, IPackedVector public override readonly bool Equals(object? obj) => obj is Rgba32 rgba32 && this.Equals(rgba32); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rgba32 other) => this.Rgba.Equals(other.Rgba); /// public override readonly string ToString() => $"Rgba32({this.R}, {this.G}, {this.B}, {this.A})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.Rgba.GetHashCode(); - /// - /// Packs a into a color returning a new instance as a result. - /// - /// The vector containing the values to pack. - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static Rgba32 PackNew(ref Vector4 vector) - { - vector *= MaxBytes; - vector += Half; - vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - - return new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W); - } - /// /// Packs the four floats into a color. /// @@ -462,39 +393,29 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The y-component /// The z-component /// The w-component - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(float x, float y, float z, float w) - { - var value = new Vector4(x, y, z, w); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Rgba32 Pack(float x, float y, float z, float w) => Pack(new Vector4(x, y, z, w)); /// /// Packs a into a uint. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector3 vector) - { - var value = new Vector4(vector, 1F); - this.Pack(ref value); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Rgba32 Pack(Vector3 vector) => Pack(new Vector4(vector, 1f)); /// /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(InliningOptions.ShortMethod)] - private void Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Rgba32 Pack(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - this.R = (byte)vector.X; - this.G = (byte)vector.Y; - this.B = (byte)vector.Z; - this.A = (byte)vector.W; + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } /// @@ -526,10 +447,10 @@ public partial struct Rgba32 : IPixel, IPackedVector return null; } - char r = hex[0]; - char g = hex[1]; - char b = hex[2]; char a = hex.Length == 3 ? 'F' : hex[3]; + char b = hex[2]; + char g = hex[1]; + char r = hex[0]; return new string(new[] { r, r, g, g, b, b, a, a }); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index b71ec5f35..5294064eb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -45,7 +45,7 @@ public partial struct Rgba64 : IPixel, IPackedVector /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(ushort r, ushort g, ushort b, ushort a) { this.R = r; @@ -58,64 +58,64 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in RGBA byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Rgba32 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); + this.A = ColorNumerics.From8BitTo16Bit(source.A); } /// /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in BGRA byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Bgra32 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); + this.A = ColorNumerics.From8BitTo16Bit(source.A); } /// /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in ARGB byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Argb32 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); + this.A = ColorNumerics.From8BitTo16Bit(source.A); } /// /// Initializes a new instance of the struct. /// /// A structure of 4 bytes in ABGR byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Abgr32 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); + this.A = ColorNumerics.From8BitTo16Bit(source.A); } /// /// Initializes a new instance of the struct. /// /// A structure of 3 bytes in RGB byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Rgb24 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); this.A = ushort.MaxValue; } @@ -123,12 +123,12 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Initializes a new instance of the struct. /// /// A structure of 3 bytes in BGR byte order. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Bgr24 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + this.R = ColorNumerics.From8BitTo16Bit(source.R); + this.G = ColorNumerics.From8BitTo16Bit(source.G); + this.B = ColorNumerics.From8BitTo16Bit(source.B); this.A = ushort.MaxValue; } @@ -136,7 +136,7 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Initializes a new instance of the struct. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Max; @@ -151,20 +151,20 @@ public partial struct Rgba64 : IPixel, IPackedVector /// public Rgb48 Rgb { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } /// public ulong PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } @@ -176,7 +176,7 @@ public partial struct Rgba64 : IPixel, IPackedVector /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba64 left, Rgba64 right) => left.PackedValue == right.PackedValue; /// @@ -187,9 +187,26 @@ public partial struct Rgba64 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + => new( + ColorNumerics.From16BitTo8Bit(this.R), + ColorNumerics.From16BitTo8Bit(this.G), + ColorNumerics.From16BitTo8Bit(this.B), + ColorNumerics.From16BitTo8Bit(this.A)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -201,181 +218,80 @@ public partial struct Rgba64 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.R = (ushort)MathF.Round(vector.X); - this.G = (ushort)MathF.Round(vector.Y); - this.B = (ushort)MathF.Round(vector.Z); - this.A = (ushort)MathF.Round(vector.W); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromVector4(Vector4 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromArgb32(Argb32 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromBgr24(Bgr24 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromBgra32(Bgra32 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromAbgr32(Abgr32 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromL8(L8 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort rgb = ColorNumerics.From8BitTo16Bit(source.PackedValue); + return new(rgb, rgb, rgb, ushort.MaxValue); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) - { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ushort.MaxValue; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromL16(L16 source) => new(source.PackedValue, source.PackedValue, source.PackedValue, ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromLa16(La16 source) { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort rgb = ColorNumerics.From8BitTo16Bit(source.L); + return new(rgb, rgb, rgb, ColorNumerics.From8BitTo16Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) - { - this.R = source.L; - this.G = source.L; - this.B = source.L; - this.A = source.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromLa32(La32 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgb24(Rgb24 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - dest.G = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - dest.B = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgba32(Rgba32 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) - { - this.Rgb = source; - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B, ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this = source; - - /// - /// Convert to . - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Rgba32 ToRgba32() - { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); - return new Rgba32(r, g, b, a); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgba64(Rgba64 source) => new(source.R, source.G, source.B, source.A); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Bgra32 ToBgra32() { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + byte r = ColorNumerics.From16BitTo8Bit(this.R); + byte g = ColorNumerics.From16BitTo8Bit(this.G); + byte b = ColorNumerics.From16BitTo8Bit(this.B); + byte a = ColorNumerics.From16BitTo8Bit(this.A); return new Bgra32(r, g, b, a); } @@ -383,13 +299,13 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Argb32 ToArgb32() { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + byte r = ColorNumerics.From16BitTo8Bit(this.R); + byte g = ColorNumerics.From16BitTo8Bit(this.G); + byte b = ColorNumerics.From16BitTo8Bit(this.B); + byte a = ColorNumerics.From16BitTo8Bit(this.A); return new Argb32(r, g, b, a); } @@ -397,13 +313,13 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Abgr32 ToAbgr32() { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + byte r = ColorNumerics.From16BitTo8Bit(this.R); + byte g = ColorNumerics.From16BitTo8Bit(this.G); + byte b = ColorNumerics.From16BitTo8Bit(this.B); + byte a = ColorNumerics.From16BitTo8Bit(this.A); return new Abgr32(r, g, b, a); } @@ -411,12 +327,12 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Rgb24 ToRgb24() { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); + byte r = ColorNumerics.From16BitTo8Bit(this.R); + byte g = ColorNumerics.From16BitTo8Bit(this.G); + byte b = ColorNumerics.From16BitTo8Bit(this.B); return new Rgb24(r, g, b); } @@ -424,12 +340,12 @@ public partial struct Rgba64 : IPixel, IPackedVector /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Bgr24 ToBgr24() { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); + byte r = ColorNumerics.From16BitTo8Bit(this.R); + byte g = ColorNumerics.From16BitTo8Bit(this.G); + byte b = ColorNumerics.From16BitTo8Bit(this.B); return new Bgr24(r, g, b); } @@ -437,13 +353,11 @@ public partial struct Rgba64 : IPixel, IPackedVector public override readonly bool Equals(object? obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Rgba64 other) => this.PackedValue.Equals(other.PackedValue); /// public override readonly string ToString() => $"Rgba64({this.R}, {this.G}, {this.B}, {this.A})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index cd61b13b2..0511f3279 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -19,49 +19,41 @@ namespace SixLabors.ImageSharp.PixelFormats; /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// +/// +/// Initializes a new instance of the struct. +/// +/// The red component. +/// The green component. +/// The blue component. +/// The alpha component. [StructLayout(LayoutKind.Sequential)] -public partial struct RgbaVector : IPixel +[method: MethodImpl(MethodImplOptions.AggressiveInlining)] +public partial struct RgbaVector(float r, float g, float b, float a = 1) : IPixel { /// /// Gets or sets the red component. /// - public float R; + public float R = r; /// /// Gets or sets the green component. /// - public float G; + public float G = g; /// /// Gets or sets the blue component. /// - public float B; + public float B = b; /// /// Gets or sets the alpha component. /// - public float A; + public float A = a; private const float MaxBytes = byte.MaxValue; private static readonly Vector4 Max = new(MaxBytes); private static readonly Vector4 Half = new(0.5F); - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] - public RgbaVector(float r, float g, float b, float a = 1) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } - /// /// Compares two objects for equality. /// @@ -70,7 +62,7 @@ public partial struct RgbaVector : IPixel /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(RgbaVector left, RgbaVector right) => left.Equals(right); /// @@ -81,20 +73,16 @@ public partial struct RgbaVector : IPixel /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(RgbaVector left, RgbaVector right) => !left.Equals(right); - /// - /// Creates a new instance of the struct. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - /// - /// The . - /// - public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.R, this.G, this.B, this.A); /// public static PixelTypeInfo GetPixelTypeInfo() @@ -107,83 +95,28 @@ public partial struct RgbaVector : IPixel public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromVector4(Vector4 source) { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.A = vector.W; + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new(source.X, source.Y, source.Z, source.W); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.R, this.G, this.B, this.A); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + /// Creates a new instance of the struct. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// + /// The . + /// + public static RgbaVector FromHex(string hex) => Color.ParseHex(hex).ToPixel(); /// /// Converts the value of this instance to a hexadecimal string. @@ -202,7 +135,6 @@ public partial struct RgbaVector : IPixel public override readonly bool Equals(object? obj) => obj is RgbaVector other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(RgbaVector other) => this.R.Equals(other.R) && this.G.Equals(other.G) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index c8819337e..e2b652d1a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-32767, -32767, 0, 1] to [32767, 32767, 0, 1] in vector form. /// /// -public partial struct Short2 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// The vector containing the component values. +public partial struct Short2(Vector2 vector) : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -33,14 +37,8 @@ public partial struct Short2 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Short2(Vector2 vector) => this.PackedValue = Pack(vector); - /// - public uint PackedValue { get; set; } + public uint PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -50,7 +48,7 @@ public partial struct Short2 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short2 left, Short2 right) => left.Equals(right); /// @@ -61,9 +59,23 @@ public partial struct Short2 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() + { + Vector2 scaled = this.ToVector2(); + scaled += new Vector2(32767f); + scaled /= 65534F; + return new Vector4(scaled, 0f, 1f); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0f, 1f); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -75,119 +87,43 @@ public partial struct Short2 : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromScaledVector4(Vector4 source) { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 65534F; + Vector2 scaled = new Vector2(source.X, source.Y) * 65534F; scaled -= new Vector2(32767F); - this.PackedValue = Pack(scaled); + return new() { PackedValue = Pack(scaled) }; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += new Vector2(32767F); - scaled /= 65534F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Short2 other) => this.PackedValue.Equals(other.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector2(); + Vector2 vector = this.ToVector2(); return FormattableString.Invariant($"Short2({vector.X:#0.##}, {vector.Y:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector = Vector2.Clamp(vector, Min, Max); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index cfd9ce0bf..18d7f22ec 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -12,7 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-37267, -37267, -37267, -37267] to [37267, 37267, 37267, 37267] in vector form. /// /// -public partial struct Short4 : IPixel, IPackedVector +/// +/// Initializes a new instance of the struct. +/// +/// A vector containing the initial values for the components. +public partial struct Short4(Vector4 vector) : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -20,8 +24,8 @@ public partial struct Short4 : IPixel, IPackedVector // Two's complement private const float MinNeg = ~(int)MaxPos; - private static readonly Vector4 Max = new Vector4(MaxPos); - private static readonly Vector4 Min = new Vector4(MinNeg); + private static readonly Vector4 Max = new(MaxPos); + private static readonly Vector4 Min = new(MinNeg); /// /// Initializes a new instance of the struct. @@ -35,14 +39,8 @@ public partial struct Short4 : IPixel, IPackedVector { } - /// - /// Initializes a new instance of the struct. - /// - /// A vector containing the initial values for the components. - public Short4(Vector4 vector) => this.PackedValue = Pack(ref vector); - /// - public ulong PackedValue { get; set; } + public ulong PackedValue { get; set; } = Pack(vector); /// /// Compares two objects for equality. @@ -52,7 +50,7 @@ public partial struct Short4 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short4 left, Short4 right) => left.Equals(right); /// @@ -63,139 +61,75 @@ public partial struct Short4 : IPixel, IPackedVector /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); - /// - public static PixelTypeInfo GetPixelTypeInfo() - => PixelTypeInfo.Create( - PixelComponentInfo.Create(4, 16, 16, 16, 16), - PixelColorType.RGB | PixelColorType.Alpha, - PixelAlphaRepresentation.Unassociated); - - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - vector *= 65534F; - vector -= new Vector4(32767F); - this.FromVector4(vector); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { - var scaled = this.ToVector4(); - scaled += new Vector4(32767F); - scaled /= 65534F; + Vector4 scaled = this.ToVector4(); + scaled += new Vector4(32767f); + scaled /= 65534f; return scaled; } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() - { - return new Vector4( + => new( (short)(this.PackedValue & 0xFFFF), (short)((this.PackedValue >> 0x10) & 0xFFFF), (short)((this.PackedValue >> 0x20) & 0xFFFF), (short)((this.PackedValue >> 0x30) & 0xFFFF)); - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromScaledVector4(Vector4 source) { - dest.FromScaledVector4(this.ToScaledVector4()); + source *= 65534F; + source -= new Vector4(32767F); + return FromVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromVector4(Vector4 source) => new(source); /// public override readonly bool Equals(object? obj) => obj is Short4 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] public readonly bool Equals(Short4 other) => this.PackedValue.Equals(other.PackedValue); /// /// Gets the hash code for the current instance. /// /// Hash code for the instance. - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// public override readonly string ToString() { - var vector = this.ToVector4(); + Vector4 vector = this.ToVector4(); return FormattableString.Invariant($"Short4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } - [MethodImpl(InliningOptions.ShortMethod)] - private static ulong Pack(ref Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Pack(Vector4 vector) { - vector = Numerics.Clamp(vector, Min, Max); - // Clamp the value between min and max values + vector = Numerics.Clamp(vector, Min, Max); ulong word4 = ((ulong)Convert.ToInt32(Math.Round(vector.X)) & 0xFFFF) << 0x00; ulong word3 = ((ulong)Convert.ToInt32(Math.Round(vector.Y)) & 0xFFFF) << 0x10; ulong word2 = ((ulong)Convert.ToInt32(Math.Round(vector.Z)) & 0xFFFF) << 0x20; diff --git a/src/ImageSharp/PixelFormats/README.md b/src/ImageSharp/PixelFormats/README.md index cbebaf23a..4c7ee545a 100644 --- a/src/ImageSharp/PixelFormats/README.md +++ b/src/ImageSharp/PixelFormats/README.md @@ -1,4 +1,4 @@ -Pixel formats adapted and extended from: +Pixel formats adapted and extended from: https://github.com/MonoGame/MonoGame diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 8d1684982..479079a80 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -117,12 +117,26 @@ public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_Conv } } - /* Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | - ---------- |------ |---------:|---------:|---------:|-------:|---------:| - ByRef | 256 | 128.5 ns | 1.217 ns | 1.138 ns | 1.00 | 0.00 | - ByVal | 256 | 196.7 ns | 2.792 ns | 2.612 ns | 1.53 | 0.02 | - FromBytes | 256 | 321.7 ns | 2.180 ns | 1.820 ns | 2.50 | 0.03 | - Inline | 256 | 129.9 ns | 2.759 ns | 2.581 ns | 1.01 | 0.02 | */ + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.100 + [Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | + |---------- |------ |-----------:|--------:|--------:|------:| + | ByRef | 256 | 102.5 ns | 0.44 ns | 0.39 ns | 1.00 | + | ByVal | 256 | 102.2 ns | 0.30 ns | 0.25 ns | 1.00 | + | FromBytes | 256 | 200.5 ns | 1.01 ns | 0.90 ns | 1.96 | + | Inline | 256 | 107.0 ns | 0.90 ns | 0.84 ns | 1.04 | + | | | | | | | + | ByRef | 2048 | 770.8 ns | 3.22 ns | 2.86 ns | 1.00 | + | ByVal | 2048 | 770.3 ns | 2.05 ns | 1.92 ns | 1.00 | + | FromBytes | 2048 | 1,546.8 ns | 7.51 ns | 6.66 ns | 2.01 | + | Inline | 2048 | 797.6 ns | 2.90 ns | 2.26 ns | 1.03 | + */ } public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 337d02084..447433dab 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -114,7 +114,7 @@ public class L16Tests // Arrange L16 gray = default; const byte rgb = 128; - ushort scaledRgb = ColorNumerics.UpscaleFrom8BitTo16Bit(rgb); + ushort scaledRgb = ColorNumerics.From8BitTo16Bit(rgb); ushort expected = ColorNumerics.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act @@ -132,7 +132,7 @@ public class L16Tests public void L16_ToRgba32(ushort input) { // Arrange - ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); + ushort expected = ColorNumerics.From16BitTo8Bit(input); L16 gray = new(input); // Act diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index f55707c05..57b70f784 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -118,7 +118,7 @@ public class La32Tests // Arrange La32 gray = default; const byte rgb = 128; - ushort scaledRgb = ColorNumerics.UpscaleFrom8BitTo16Bit(rgb); + ushort scaledRgb = ColorNumerics.From8BitTo16Bit(rgb); ushort expected = ColorNumerics.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act @@ -137,7 +137,7 @@ public class La32Tests public void La32_ToRgba32(ushort input) { // Arrange - ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); + ushort expected = ColorNumerics.From16BitTo8Bit(input); La32 gray = new(input, ushort.MaxValue); // Act diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index 006cb9eb6..413492281 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -104,7 +104,7 @@ public abstract partial class PixelConverterTests { ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref l16Ref, i); - dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + dp.Pack(sp.ToScaledVector4()); } return; From dfd983f17c6975aaad6877a74a945ff71ca23b0e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Jan 2024 20:59:33 +1000 Subject: [PATCH 055/220] Update library to use new pixel API --- src/ImageSharp.ruleset | 5 +- src/ImageSharp/Color/Color.cs | 15 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 210 +++--- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 10 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 7 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 9 +- .../Formats/Png/PngScanlineProcessor.cs | 119 +-- src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs | 14 +- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 100 ++- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 10 +- .../Tiff/Compression/HorizontalPredictor.cs | 140 ++-- .../BlackIsZero16TiffColor{TPixel}.cs | 10 +- .../BlackIsZero1TiffColor{TPixel}.cs | 6 +- .../BlackIsZero24TiffColor{TPixel}.cs | 12 +- .../BlackIsZero32FloatTiffColor{TPixel}.cs | 11 +- .../BlackIsZero32TiffColor{TPixel}.cs | 13 +- .../BlackIsZero4TiffColor{TPixel}.cs | 25 +- .../BlackIsZeroTiffColor{TPixel}.cs | 13 +- .../CieLabPlanarTiffColor{TPixel}.cs | 11 +- .../CieLabTiffColor{TPixel}.cs | 10 +- .../CmykTiffColor{TPixel}.cs | 8 +- .../PaletteTiffColor{TPixel}.cs | 13 +- .../Rgb161616TiffColor{TPixel}.cs | 15 +- .../Rgb16PlanarTiffColor{TPixel}.cs | 22 +- .../Rgb242424TiffColor{TPixel}.cs | 20 +- .../Rgb24PlanarTiffColor{TPixel}.cs | 20 +- .../Rgb323232TiffColor{TPixel}.cs | 20 +- .../Rgb32PlanarTiffColor{TPixel}.cs | 21 +- .../Rgb444TiffColor{TPixel}.cs | 12 +- .../RgbFloat323232TiffColor{TPixel}.cs | 12 +- .../RgbPlanarTiffColor{TPixel}.cs | 13 +- .../RgbTiffColor{TPixel}.cs | 8 +- .../Rgba16161616TiffColor{TPixel}.cs | 23 +- .../Rgba16PlanarTiffColor{TPixel}.cs | 34 +- .../Rgba24242424TiffColor{TPixel}.cs | 33 +- .../Rgba24PlanarTiffColor{TPixel}.cs | 32 +- .../Rgba32323232TiffColor{TPixel}.cs | 33 +- .../Rgba32PlanarTiffColor{TPixel}.cs | 34 +- .../Rgba8888TiffColor{TPixel}.cs | 5 +- .../RgbaFloat32323232TiffColor{TPixel}.cs | 12 +- .../RgbaPlanarTiffColor{TPixel}.cs | 18 +- .../RgbaTiffColor{TPixel}.cs | 12 +- .../WhiteIsZero16TiffColor{TPixel}.cs | 14 +- .../WhiteIsZero1TiffColor{TPixel}.cs | 7 +- .../WhiteIsZero24TiffColor{TPixel}.cs | 12 +- .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 11 +- .../WhiteIsZero32TiffColor{TPixel}.cs | 12 +- .../WhiteIsZero4TiffColor{TPixel}.cs | 25 +- .../WhiteIsZero8TiffColor{TPixel}.cs | 8 +- .../WhiteIsZeroTiffColor{TPixel}.cs | 12 +- .../YCbCrPlanarTiffColor{TPixel}.cs | 12 +- .../YCbCrTiffColor{TPixel}.cs | 11 +- .../Formats/Tiff/TiffDecoderCore.cs | 6 +- .../Formats/Tiff/Utils/TiffUtilities.cs | 120 ++++ .../Formats/Tiff/Utils/TiffUtils.cs | 176 ----- .../Formats/Webp/Lossy/WebpLossyDecoder.cs | 4 +- src/ImageSharp/PixelFormats/IPixel.cs | 40 +- .../DefaultPixelBlenders.Generated.cs | 432 +++-------- .../DefaultPixelBlenders.Generated.tt | 4 +- .../PorterDuffFunctions.Generated.cs | 432 +++-------- .../PorterDuffFunctions.Generated.tt | 4 +- .../PixelFormats/PixelConversionModifiers.cs | 8 +- .../PixelFormats/PixelImplementations/A8.cs | 12 +- .../PixelImplementations/Abgr32.cs | 20 +- .../PixelImplementations/Argb32.cs | 41 +- .../PixelImplementations/Bgr24.cs | 43 +- .../PixelImplementations/Bgr565.cs | 55 ++ .../PixelImplementations/Bgra32.cs | 10 +- .../PixelImplementations/Bgra4444.cs | 70 +- .../PixelImplementations/Bgra5551.cs | 72 +- .../PixelImplementations/Byte4.cs | 80 ++- .../PixelImplementations/HalfSingle.cs | 70 +- .../PixelImplementations/HalfVector2.cs | 56 ++ .../PixelImplementations/HalfVector4.cs | 74 +- .../PixelFormats/PixelImplementations/L16.cs | 26 +- .../PixelFormats/PixelImplementations/L8.cs | 29 +- .../PixelFormats/PixelImplementations/La16.cs | 34 +- .../PixelFormats/PixelImplementations/La32.cs | 40 +- .../PixelImplementations/NormalizedByte2.cs | 70 +- .../PixelImplementations/NormalizedByte4.cs | 70 +- .../PixelImplementations/NormalizedShort2.cs | 70 +- .../PixelImplementations/NormalizedShort4.cs | 70 +- .../PixelOperations/A8.PixelOperations.cs | 2 - .../PixelOperations/Abgr32.PixelOperations.cs | 2 - .../PixelOperations/Argb32.PixelOperations.cs | 2 - .../PixelOperations/Bgr24.PixelOperations.cs | 2 - .../PixelOperations/Bgr565.PixelOperations.cs | 2 - .../PixelOperations/Bgra32.PixelOperations.cs | 2 - .../Bgra4444.PixelOperations.cs | 2 - .../Bgra5551.PixelOperations.cs | 2 - .../PixelOperations/Byte4.PixelOperations.cs | 2 - .../Abgr32.PixelOperations.Generated.cs | 267 ++++--- .../Argb32.PixelOperations.Generated.cs | 267 ++++--- .../Bgr24.PixelOperations.Generated.cs | 267 ++++--- .../Bgra32.PixelOperations.Generated.cs | 267 ++++--- .../Bgra5551.PixelOperations.Generated.cs | 222 +++--- .../L16.PixelOperations.Generated.cs | 222 +++--- .../Generated/L8.PixelOperations.Generated.cs | 222 +++--- .../La16.PixelOperations.Generated.cs | 222 +++--- .../La32.PixelOperations.Generated.cs | 222 +++--- .../Rgb24.PixelOperations.Generated.cs | 267 ++++--- .../Rgb48.PixelOperations.Generated.cs | 222 +++--- .../Rgba32.PixelOperations.Generated.cs | 257 +++---- .../Rgba64.PixelOperations.Generated.cs | 222 +++--- .../Generated/_Common.ttinclude | 69 +- .../HalfSingle.PixelOperations.cs | 2 - .../HalfVector2.PixelOperations.cs | 2 - .../HalfVector4.PixelOperations.cs | 2 - .../PixelOperations/L16.PixelOperations.cs | 2 - .../PixelOperations/L8.PixelOperations.cs | 2 - .../PixelOperations/La16.PixelOperations.cs | 2 - .../PixelOperations/La32.PixelOperations.cs | 2 - .../NormalizedByte2.PixelOperations.cs | 2 - .../NormalizedByte4.PixelOperations.cs | 2 - .../NormalizedShort2.PixelOperations.cs | 2 - .../NormalizedShort4.PixelOperations.cs | 2 - .../PixelOperations/Rg32.PixelOperations.cs | 2 - .../PixelOperations/Rgb24.PixelOperations.cs | 2 - .../PixelOperations/Rgb48.PixelOperations.cs | 2 - .../PixelOperations/Rgba32.PixelOperations.cs | 1 - .../PixelOperations/Rgba64.PixelOperations.cs | 2 - .../RgbaVector.PixelOperations.cs | 40 -- .../PixelOperations/Short2.PixelOperations.cs | 2 - .../PixelOperations/Short4.PixelOperations.cs | 2 - .../PixelFormats/PixelImplementations/Rg32.cs | 24 +- .../PixelImplementations/Rgb24.cs | 49 +- .../PixelImplementations/Rgb48.cs | 23 +- .../PixelImplementations/Rgba1010102.cs | 70 +- .../PixelImplementations/Rgba32.cs | 37 +- .../PixelImplementations/Rgba64.cs | 60 +- .../PixelImplementations/RgbaVector.cs | 90 ++- .../PixelImplementations/Short2.cs | 70 +- .../PixelImplementations/Short4.cs | 70 +- .../PixelOperations{TPixel}.Generated.cs | 676 ++++++++---------- .../PixelOperations{TPixel}.Generated.tt | 52 +- .../PixelFormats/PixelOperations{TPixel}.cs | 70 +- .../Utils/Vector4Converters.Default.cs | 92 +-- .../EdgeDetectorCompassProcessor{TPixel}.cs | 5 +- .../Processors/Dithering/ErrorDither.cs | 2 +- .../Processors/Dithering/OrderedDither.cs | 8 +- ...eHistogramEqualizationProcessor{TPixel}.cs | 25 +- ...alizationSlidingWindowProcessor{TPixel}.cs | 2 +- .../Quantization/EuclideanPixelMap{TPixel}.cs | 6 +- .../Quantization/OctreeQuantizer{TPixel}.cs | 7 +- .../Quantization/WuQuantizer{TPixel}.cs | 8 +- .../Bulk/FromRgba32Bytes.cs | 12 +- .../ImageSharp.Benchmarks/Bulk/FromVector4.cs | 10 +- .../Bulk/ToRgba32Bytes.cs | 3 +- .../PixelConversion_Rgba32_To_Argb32.cs | 44 +- .../PixelConversion_Rgba32_To_Bgra32.cs | 58 +- tests/ImageSharp.Tests/Color/RgbaDouble.cs | 141 ++-- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 22 +- .../Jpg/Utils/LibJpegTools.ComponentData.cs | 13 +- .../Jpg/Utils/LibJpegTools.SpectralData.cs | 43 +- .../Formats/Png/PngEncoderTests.cs | 2 +- .../Formats/WebP/PredictorEncoderTests.cs | 10 +- tests/ImageSharp.Tests/Issues/Issue594.cs | 168 +---- .../ImageSharp.Tests/PixelFormats/A8Tests.cs | 13 +- .../PixelFormats/Abgr32Tests.cs | 11 +- .../PixelFormats/Argb32Tests.cs | 20 +- .../PixelFormats/Bgr24Tests.cs | 11 +- .../PixelFormats/Bgr565Tests.cs | 65 +- .../PixelFormats/Bgra32Tests.cs | 11 +- .../PixelFormats/Bgra4444Tests.cs | 82 +-- .../PixelFormats/Bgra5551Tests.cs | 110 ++- .../PixelFormats/Byte4Tests.cs | 93 ++- .../PixelFormats/HalfSingleTests.cs | 15 +- .../PixelFormats/HalfVector2Tests.cs | 12 +- .../PixelFormats/HalfVector4Tests.cs | 16 +- .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 35 +- .../ImageSharp.Tests/PixelFormats/L8Tests.cs | 70 +- .../PixelFormats/La16Tests.cs | 57 +- .../PixelFormats/La32Tests.cs | 43 +- .../PixelFormats/NormalizedByte2Tests.cs | 16 +- .../PixelFormats/NormalizedByte4Tests.cs | 65 +- .../PixelFormats/NormalizedShort2Tests.cs | 8 +- .../PixelFormats/NormalizedShort4Tests.cs | 63 +- ...ConverterTests.ReferenceImplementations.cs | 43 +- .../PixelOperations/PixelOperationsTests.cs | 206 +++--- .../PixelFormats/Rg32Tests.cs | 14 +- .../PixelFormats/Rgb24Tests.cs | 14 +- .../PixelFormats/Rgb48Tests.cs | 11 +- .../PixelFormats/Rgba1010102Tests.cs | 76 +- .../PixelFormats/Rgba32Tests.cs | 49 +- .../PixelFormats/Rgba64Tests.cs | 24 +- .../PixelFormats/RgbaVectorTests.cs | 27 +- .../PixelFormats/Short2Tests.cs | 29 +- .../PixelFormats/Short4Tests.cs | 54 +- .../PixelFormats/UnPackedPixelTests.cs | 6 +- .../Processing/IntegralImageTests.cs | 22 +- .../Transforms/AffineTransformTests.cs | 137 ++-- tests/ImageSharp.Tests/TestFormat.cs | 74 +- .../BasicTestPatternProvider.cs | 27 +- .../ImageProviders/TestPatternProvider.cs | 79 +- .../TestUtilities/ImagingTestCaseUtility.cs | 11 +- .../TestUtilities/TestImageExtensions.cs | 9 +- .../TestUtilities/TestPixel.cs | 9 +- .../TestUtilities/TestUtils.cs | 35 +- .../Tests/TestImageProviderTests.cs | 4 +- .../Tests/TestUtilityExtensionsTests.cs | 9 +- 200 files changed, 4803 insertions(+), 5794 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs delete mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index f29278c95..72e0cd1b0 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,6 +1,7 @@  - + + - + \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 3b91a78d1..82ecab390 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -248,15 +248,12 @@ public readonly partial struct Color : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public string ToHex() { - Rgba32 rgba = default; if (this.boxedHighPrecisionPixel is not null) { - this.boxedHighPrecisionPixel.ToRgba32(ref rgba); - return rgba.ToHex(); + return this.boxedHighPrecisionPixel.ToRgba32().ToHex(); } - rgba.FromScaledVector4(this.data); - return rgba.ToHex(); + return Rgba32.FromScaledVector4(this.data).ToHex(); } /// @@ -278,14 +275,10 @@ public readonly partial struct Color : IEquatable if (this.boxedHighPrecisionPixel is null) { - pixel = default; - pixel.FromScaledVector4(this.data); - return pixel; + return TPixel.FromScaledVector4(this.data); } - pixel = default; - pixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); - return pixel; + return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 863fed359..bed489752 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -294,72 +294,60 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals private void ReadRle(BufferedReadStream stream, BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { - TPixel color = default; - using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) - using (IMemoryOwner undefinedPixels = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) - using (IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean)) + using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean); + using IMemoryOwner undefinedPixels = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean); + using IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean); + Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; + Span undefinedPixelsSpan = undefinedPixels.Memory.Span; + Span bufferSpan = buffer.Memory.Span; + if (compression is BmpCompression.RLE8) { - Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; - Span undefinedPixelsSpan = undefinedPixels.Memory.Span; - Span bufferSpan = buffer.Memory.Span; - if (compression is BmpCompression.RLE8) - { - this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); - } - else - { - this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); - } + this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + } + else + { + this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + } - for (int y = 0; y < height; y++) - { - int newY = Invert(y, height, inverted); - int rowStartIdx = y * width; - Span bufferRow = bufferSpan.Slice(rowStartIdx, width); - Span pixelRow = pixels.DangerousGetRowSpan(newY); + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + int rowStartIdx = y * width; + Span bufferRow = bufferSpan.Slice(rowStartIdx, width); + Span pixelRow = pixels.DangerousGetRowSpan(newY); - bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; - if (rowHasUndefinedPixels) + bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; + if (rowHasUndefinedPixels) + { + // Slow path with undefined pixels. + for (int x = 0; x < width; x++) { - // Slow path with undefined pixels. - for (int x = 0; x < width; x++) + byte colorIdx = bufferRow[x]; + if (undefinedPixelsSpan[rowStartIdx + x]) { - byte colorIdx = bufferRow[x]; - if (undefinedPixelsSpan[rowStartIdx + x]) - { - switch (this.rleSkippedPixelHandling) - { - case RleSkippedPixelHandling.FirstColorOfPalette: - color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); - break; - case RleSkippedPixelHandling.Transparent: - color.FromScaledVector4(Vector4.Zero); - break; - - // Default handling for skipped pixels is black (which is what System.Drawing is also doing). - default: - color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); - break; - } - } - else + pixelRow[x] = this.rleSkippedPixelHandling switch { - color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); - } + RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])), + RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero), - pixelRow[x] = color; + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). + _ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)), + }; } - } - else - { - // Fast path without any undefined pixels. - for (int x = 0; x < width; x++) + else { - color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); } } } + else + { + // Fast path without any undefined pixels. + for (int x = 0; x < width; x++) + { + pixelRow[x] = TPixel.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); + } + } } } @@ -375,66 +363,54 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals private void ReadRle24(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { - TPixel color = default; - using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * 3, AllocationOptions.Clean)) - using (IMemoryOwner undefinedPixels = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) - using (IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean)) - { - Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; - Span undefinedPixelsSpan = undefinedPixels.Memory.Span; - Span bufferSpan = buffer.GetSpan(); + using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * 3, AllocationOptions.Clean); + using IMemoryOwner undefinedPixels = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean); + using IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean); + Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; + Span undefinedPixelsSpan = undefinedPixels.Memory.Span; + Span bufferSpan = buffer.GetSpan(); - this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); - for (int y = 0; y < height; y++) + this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.DangerousGetRowSpan(newY); + bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; + if (rowHasUndefinedPixels) { - int newY = Invert(y, height, inverted); - Span pixelRow = pixels.DangerousGetRowSpan(newY); - bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; - if (rowHasUndefinedPixels) + // Slow path with undefined pixels. + int yMulWidth = y * width; + int rowStartIdx = yMulWidth * 3; + for (int x = 0; x < width; x++) { - // Slow path with undefined pixels. - int yMulWidth = y * width; - int rowStartIdx = yMulWidth * 3; - for (int x = 0; x < width; x++) + int idx = rowStartIdx + (x * 3); + if (undefinedPixelsSpan[yMulWidth + x]) { - int idx = rowStartIdx + (x * 3); - if (undefinedPixelsSpan[yMulWidth + x]) - { - switch (this.rleSkippedPixelHandling) - { - case RleSkippedPixelHandling.FirstColorOfPalette: - color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); - break; - case RleSkippedPixelHandling.Transparent: - color.FromScaledVector4(Vector4.Zero); - break; - - // Default handling for skipped pixels is black (which is what System.Drawing is also doing). - default: - color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); - break; - } - } - else + pixelRow[x] = this.rleSkippedPixelHandling switch { - color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); - } + RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As(ref bufferSpan[idx])), + RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero), - pixelRow[x] = color; + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). + _ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)), + }; } - } - else - { - // Fast path without any undefined pixels. - int rowStartIdx = y * width * 3; - for (int x = 0; x < width; x++) + else { - int idx = rowStartIdx + (x * 3); - color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromBgr24(Unsafe.As(ref bufferSpan[idx])); } } } + else + { + // Fast path without any undefined pixels. + int rowStartIdx = y * width * 3; + for (int x = 0; x < width; x++) + { + int idx = rowStartIdx + (x * 3); + pixelRow[x] = TPixel.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + } + } } } @@ -492,7 +468,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals int max = cmd[1]; int bytesToRead = (int)(((uint)max + 1) / 2); - Span run = bytesToRead <= 128 ? scratchBuffer.Slice(0, bytesToRead) : new byte[bytesToRead]; + Span run = bytesToRead <= 128 ? scratchBuffer[..bytesToRead] : new byte[bytesToRead]; stream.Read(run); @@ -598,7 +574,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Take this number of bytes from the stream as uncompressed data. int length = cmd[1]; - Span run = length <= 128 ? scratchBuffer.Slice(0, length) : new byte[length]; + Span run = length <= 128 ? scratchBuffer[..length] : new byte[length]; stream.Read(run); @@ -680,7 +656,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals int length = cmd[1]; int length3 = length * 3; - Span run = length3 <= 128 ? scratchBuffer.Slice(0, length3) : new byte[length3]; + Span run = length3 <= 128 ? scratchBuffer[..length3] : new byte[length3]; stream.Read(run); @@ -835,7 +811,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } using IMemoryOwner row = this.memoryAllocator.Allocate(arrayWidth + padding, AllocationOptions.Clean); - TPixel color = default; Span rowSpan = row.GetSpan(); for (int y = 0; y < height; y++) @@ -856,8 +831,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; - color.FromBgr24(Unsafe.As(ref colors[colorIndex])); - pixelRow[newX] = color; + pixelRow[newX] = TPixel.FromBgr24(Unsafe.As(ref colors[colorIndex])); } offset++; @@ -882,8 +856,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { int padding = CalculatePadding(width, 2); int stride = (width * 2) + padding; - TPixel color = default; - int rightShiftRedMask = CalculateRightShift((uint)redMask); int rightShiftGreenMask = CalculateRightShift((uint)greenMask); int rightShiftBlueMask = CalculateRightShift((uint)blueMask); @@ -917,8 +889,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask); Rgb24 rgb = new((byte)r, (byte)g, (byte)b); - color.FromRgb24(rgb); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromRgb24(rgb); offset += 2; } } @@ -1107,8 +1078,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { Bgra32 bgra = bgraRowSpan[x]; bgra.A = byte.MaxValue; - ref TPixel pixel = ref pixelSpan[x]; - pixel.FromBgra32(bgra); + pixelSpan[x] = TPixel.FromBgra32(bgra); } } } @@ -1129,7 +1099,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals 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; int padding = CalculatePadding(width, 4); int stride = (width * 4) + padding; @@ -1179,18 +1148,17 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals g * invMaxValueGreen, b * invMaxValueBlue, alpha); - color.FromScaledVector4(vector4); + pixelRow[x] = TPixel.FromScaledVector4(vector4); } else { byte r = (byte)((temp & redMask) >> rightShiftRedMask); byte g = (byte)((temp & greenMask) >> rightShiftGreenMask); byte b = (byte)((temp & blueMask) >> rightShiftBlueMask); - byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255; - color.FromRgba32(new Rgba32(r, g, b, a)); + byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : byte.MaxValue; + pixelRow[x] = TPixel.FromRgba32(new(r, g, b, a)); } - pixelRow[x] = color; offset += 4; } } @@ -1468,7 +1436,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry; } - palette = Array.Empty(); + palette = []; if (colorMapSizeBytes > 0) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 8916da6e0..d64792eba 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -185,7 +185,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { uint frameCount = 0; ImageFrameMetadata? previousFrame = null; - List framesMetadata = new(); + List framesMetadata = []; try { this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); @@ -595,9 +595,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); - Rgb24 rgb = colorTable[index]; - pixel.FromRgb24(rgb); + Unsafe.Add(ref rowRef, (uint)x) = TPixel.FromRgb24(colorTable[index]); } } else @@ -613,9 +611,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } int index = Numerics.Clamp(rawIndex, 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); - Rgb24 rgb = colorTable[index]; - pixel.FromRgb24(rgb); + Unsafe.Add(ref rowRef, (uint)x) = TPixel.FromRgb24(colorTable[index]); } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 1215768e4..95429e606 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -169,7 +169,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals this.EncodeFirstFrame(stream, frameMetadata, quantized); // Capture the global palette for reuse on subsequent frames and cleanup the quantized frame. - TPixel[] globalPalette = image.Frames.Count == 1 ? Array.Empty() : quantized.Palette.ToArray(); + TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray(); this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMethod); @@ -488,8 +488,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals int index = -1; if (quantized != null) { - TPixel transparentPixel = default; - transparentPixel.FromScaledVector4(Vector4.Zero); + TPixel transparentPixel = TPixel.FromScaledVector4(Vector4.Zero); ReadOnlySpan palette = quantized.Palette.Span; // Transparent pixels are much more likely to be found at the end of a palette. @@ -693,7 +692,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals } IMemoryOwner? owner = null; - Span extensionBuffer = stackalloc byte[0]; // workaround compiler limitation + scoped Span extensionBuffer = []; // workaround compiler limitation if (extensionSize > 128) { owner = this.memoryAllocator.Allocate(extensionSize + 3); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index aa3603cfb..993f53269 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -328,18 +328,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable => clone.ProcessPixelRows(accessor => { // TODO: We should be able to speed this up with SIMD and masking. - Rgba32 rgba32 = default; Rgba32 transparent = Color.Transparent.ToPixel(); for (int y = 0; y < accessor.Height; y++) { Span span = accessor.GetRowSpan(y); for (int x = 0; x < accessor.Width; x++) { - span[x].ToRgba32(ref rgba32); - - if (rgba32.A is 0) + ref TPixel pixel = ref span[x]; + Rgba32 rgba = pixel.ToRgba32(); + if (rgba.A is 0) { - span[x].FromRgba32(transparent); + pixel = TPixel.FromRgba32(transparent); } } } diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index 39b3fff27..aa937a8e2 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -42,7 +42,6 @@ internal static class PngScanlineProcessor where TPixel : unmanaged, IPixel { uint offset = pixelOffset + frameControl.XOffset; - TPixel pixel = default; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(bitDepth) - 1); @@ -55,8 +54,7 @@ internal static class PngScanlineProcessor for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - pixel.FromL16(Unsafe.As(ref luminance)); - Unsafe.Add(ref rowSpanRef, x) = pixel; + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromL16(Unsafe.As(ref luminance)); } } else @@ -64,8 +62,7 @@ internal static class PngScanlineProcessor for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); - pixel.FromL8(Unsafe.As(ref luminance)); - Unsafe.Add(ref rowSpanRef, x) = pixel; + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromL8(Unsafe.As(ref luminance)); } } @@ -75,30 +72,22 @@ internal static class PngScanlineProcessor if (bitDepth == 16) { L16 transparent = transparentColor.Value.ToPixel(); - La32 source = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - source.L = luminance; - source.A = luminance.Equals(transparent.PackedValue) ? ushort.MinValue : ushort.MaxValue; - - pixel.FromLa32(source); - Unsafe.Add(ref rowSpanRef, x) = pixel; + La32 source = new(luminance, luminance.Equals(transparent.PackedValue) ? ushort.MinValue : ushort.MaxValue); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa32(source); } } else { byte transparent = (byte)(transparentColor.Value.ToPixel().PackedValue * scaleFactor); - La16 source = default; for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); - source.L = luminance; - source.A = luminance.Equals(transparent) ? byte.MinValue : byte.MaxValue; - - pixel.FromLa16(source); - Unsafe.Add(ref rowSpanRef, x) = pixel; + La16 source = new(luminance, luminance.Equals(transparent) ? byte.MinValue : byte.MaxValue); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa16(source); } } } @@ -133,34 +122,28 @@ internal static class PngScanlineProcessor where TPixel : unmanaged, IPixel { uint offset = pixelOffset + frameControl.XOffset; - TPixel pixel = default; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); if (bitDepth == 16) { - La32 source = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += 4) { - source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); + ushort l = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - pixel.FromLa32(source); - Unsafe.Add(ref rowSpanRef, (uint)x) = pixel; + Unsafe.Add(ref rowSpanRef, (uint)x) = TPixel.FromLa32(new(l, a)); } } else { - La16 source = default; nuint offset2 = 0; for (nuint x = offset; x < frameControl.XMax; x += increment) { - source.L = Unsafe.Add(ref scanlineSpanRef, offset2); - source.A = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample); - - pixel.FromLa16(source); - Unsafe.Add(ref rowSpanRef, x) = pixel; + byte l = Unsafe.Add(ref scanlineSpanRef, offset2); + byte a = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa16(new(l, a)); offset2 += bytesPerPixel; } } @@ -194,7 +177,6 @@ internal static class PngScanlineProcessor PngThrowHelper.ThrowMissingPalette(); } - TPixel pixel = default; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref Color paletteBase = ref MemoryMarshal.GetReference(palette.Value.Span); @@ -202,8 +184,7 @@ internal static class PngScanlineProcessor for (nuint x = pixelOffset, o = 0; x < frameControl.XMax; x += increment, o++) { uint index = Unsafe.Add(ref scanlineSpanRef, o); - pixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToPixel()); - Unsafe.Add(ref rowSpanRef, x) = pixel; + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToPixel()); } } @@ -243,8 +224,6 @@ internal static class PngScanlineProcessor where TPixel : unmanaged, IPixel { uint offset = pixelOffset + frameControl.XOffset; - - TPixel pixel = default; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); @@ -252,16 +231,13 @@ internal static class PngScanlineProcessor { if (bitDepth == 16) { - Rgb48 rgb48 = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); - - pixel.FromRgb48(rgb48); - Unsafe.Add(ref rowSpanRef, x) = pixel; + ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgb48(new(r, g, b)); } } else if (pixelOffset == 0 && increment == 1) @@ -274,16 +250,13 @@ internal static class PngScanlineProcessor } else { - Rgb24 rgb = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - rgb.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); - rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); - rgb.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); - - pixel.FromRgb24(rgb); - Unsafe.Add(ref rowSpanRef, x) = pixel; + byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o); + byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgb24(new(r, g, b)); } } @@ -293,27 +266,20 @@ internal static class PngScanlineProcessor if (bitDepth == 16) { Rgb48 transparent = transparentColor.Value.ToPixel(); - - Rgb48 rgb48 = default; - Rgba64 rgba64 = default; + Rgba64 rgba = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); - - rgba64.Rgb = rgb48; - rgba64.A = rgb48.Equals(transparent) ? ushort.MinValue : ushort.MaxValue; - - pixel.FromRgba64(rgba64); - Unsafe.Add(ref rowSpanRef, x) = pixel; + rgba.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgba.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgba.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + rgba.A = rgba.Rgb.Equals(transparent) ? ushort.MinValue : ushort.MaxValue; + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(rgba); } } else { Rgb24 transparent = transparentColor.Value.ToPixel(); - Rgba32 rgba = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) @@ -322,9 +288,7 @@ internal static class PngScanlineProcessor rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); rgba.A = transparent.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; - - pixel.FromRgba32(rgba); - Unsafe.Add(ref rowSpanRef, x) = pixel; + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(rgba); } } } @@ -362,22 +326,18 @@ internal static class PngScanlineProcessor where TPixel : unmanaged, IPixel { uint offset = pixelOffset + frameControl.XOffset; - TPixel pixel = default; ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); if (bitDepth == 16) { - Rgba64 rgba64 = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); - rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); - rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); - rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); - - pixel.FromRgba64(rgba64); - Unsafe.Add(ref rowSpanRef, x) = pixel; + ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(new(r, g, b, a)); } } else if (pixelOffset == 0 && increment == 1) @@ -391,17 +351,14 @@ internal static class PngScanlineProcessor else { ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); - Rgba32 rgba = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); - rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); - rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); - rgba.A = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); - - pixel.FromRgba32(rgba); - Unsafe.Add(ref rowSpanRef, x) = pixel; + byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o); + byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); + byte a = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(new(r, g, b, a)); } } } diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index deb0a37f0..86d81d834 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -149,7 +149,7 @@ internal class QoiDecoderCore : IImageDecoderInternals Span previouslySeenPixels = previouslySeenPixelsBuffer.GetSpan(); Rgba32 previousPixel = new(0, 0, 0, 255); - // We save the pixel to avoid loosing the fully opaque black pixel + // We save the pixel to avoid losing the fully opaque black pixel // See https://github.com/phoboslab/qoi/issues/258 int pixelArrayPosition = GetArrayPosition(previousPixel); previouslySeenPixels[pixelArrayPosition] = previousPixel; @@ -174,7 +174,7 @@ internal class QoiDecoderCore : IImageDecoderInternals } readPixel.A = previousPixel.A; - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); pixelArrayPosition = GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; @@ -186,7 +186,7 @@ internal class QoiDecoderCore : IImageDecoderInternals ThrowInvalidImageContentException(); } - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); pixelArrayPosition = GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; @@ -197,7 +197,7 @@ internal class QoiDecoderCore : IImageDecoderInternals // Getting one pixel from previously seen pixels case QoiChunk.QoiOpIndex: readPixel = previouslySeenPixels[operationByte]; - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); break; // Get one pixel from the difference (-2..1) of the previous pixel @@ -211,7 +211,7 @@ internal class QoiDecoderCore : IImageDecoderInternals G = (byte)Numerics.Modulo256(previousPixel.G + (greenDifference - 2)), B = (byte)Numerics.Modulo256(previousPixel.B + (blueDifference - 2)) }; - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); pixelArrayPosition = GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; @@ -227,7 +227,7 @@ internal class QoiDecoderCore : IImageDecoderInternals int currentRed = Numerics.Modulo256(diffRedDG - 8 + (diffGreen - 32) + previousPixel.R); int currentBlue = Numerics.Modulo256(diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B); readPixel = previousPixel with { R = (byte)currentRed, B = (byte)currentBlue, G = (byte)currentGreen }; - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); pixelArrayPosition = GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; @@ -241,7 +241,7 @@ internal class QoiDecoderCore : IImageDecoderInternals } readPixel = previousPixel; - pixel.FromRgba32(readPixel); + pixel = TPixel.FromRgba32(readPixel); for (int k = -1; k < repetitions; k++, j++) { if (j == row.Length) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 26e057bff..de99c475b 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -222,7 +222,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void ReadPaletted(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - TPixel color = default; bool invertX = InvertX(origin); for (int y = 0; y < height; y++) @@ -237,14 +236,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } @@ -255,14 +254,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } else { for (int x = 0; x < width; x++) { - ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } @@ -273,14 +272,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } else { for (int x = 0; x < width; x++) { - ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow); } } @@ -319,16 +318,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals switch (colorMapPixelSizeInBytes) { case 1: - color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + color = TPixel.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 2: - this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); + color = this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes); break; case 3: - color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + color = TPixel.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; case 4: - color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + color = TPixel.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); break; } @@ -350,17 +349,15 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void ReadMonoChrome(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool invertX = InvertX(origin); - if (invertX) + if (InvertX(origin)) { - TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - ReadL8Pixel(stream, color, x, pixelSpan); + ReadL8Pixel(stream, x, pixelSpan); } } @@ -369,8 +366,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0); Span rowSpan = row.GetSpan(); - bool invertY = InvertY(origin); - if (invertY) + if (InvertY(origin)) { for (int y = height - 1; y >= 0; y--) { @@ -398,7 +394,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void ReadBgra16(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - TPixel color = default; bool invertX = InvertX(origin); using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); Span rowSpan = row.GetSpan(); @@ -426,14 +421,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { - color.FromLa16(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); + pixelSpan[x] = TPixel.FromLa16(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); } else { - color.FromBgra5551(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); + pixelSpan[x] = TPixel.FromBgra5551(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); } - - pixelSpan[x] = color; } } else @@ -477,18 +470,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void ReadBgr24(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool invertX = InvertX(origin); - if (invertX) + if (InvertX(origin)) { Span scratchBuffer = stackalloc byte[4]; - TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - ReadBgr24Pixel(stream, color, x, pixelSpan, scratchBuffer); + ReadBgr24Pixel(stream, x, pixelSpan, scratchBuffer); } } @@ -497,9 +488,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0); Span rowSpan = row.GetSpan(); - bool invertY = InvertY(origin); - if (invertY) + if (InvertY(origin)) { for (int y = height - 1; y >= 0; y--) { @@ -527,7 +517,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals 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); @@ -565,14 +554,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); + this.ReadBgra32Pixel(stream, x, pixelRow, scratchBuffer); } } else { for (int x = 0; x < width; x++) { - this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); + this.ReadBgra32Pixel(stream, x, pixelRow, scratchBuffer); } } } @@ -610,7 +599,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals switch (bytesPerPixel) { case 1: - color.FromL8(Unsafe.As(ref bufferSpan[idx])); + color = TPixel.FromL8(Unsafe.As(ref bufferSpan[idx])); break; case 2: if (!this.hasAlpha) @@ -621,26 +610,26 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) { - color.FromLa16(Unsafe.As(ref bufferSpan[idx])); + color = TPixel.FromLa16(Unsafe.As(ref bufferSpan[idx])); } else { - color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + color = TPixel.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); } break; case 3: - color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + color = TPixel.FromBgr24(Unsafe.As(ref bufferSpan[idx])); break; case 4: if (this.hasAlpha) { - color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + color = TPixel.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)); + color = TPixel.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha)); } break; @@ -677,16 +666,15 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReadL8Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) + private static void ReadL8Pixel(BufferedReadStream stream, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { byte pixelValue = (byte)stream.ReadByte(); - color.FromL8(Unsafe.As(ref pixelValue)); - pixelSpan[x] = color; + pixelSpan[x] = TPixel.FromL8(Unsafe.As(ref pixelValue)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReadBgr24Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan, Span scratchBuffer) + private static void ReadBgr24Pixel(BufferedReadStream stream, int x, Span pixelSpan, Span scratchBuffer) where TPixel : unmanaged, IPixel { int bytesRead = stream.Read(scratchBuffer, 0, 3); @@ -695,8 +683,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); } - color.FromBgr24(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); - pixelSpan[x] = color; + pixelSpan[x] = TPixel.FromBgr24(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -714,7 +701,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgra32Pixel(BufferedReadStream stream, int x, TPixel color, Span pixelRow, Span scratchBuffer) + private void ReadBgra32Pixel(BufferedReadStream stream, int x, Span pixelRow, Span scratchBuffer) where TPixel : unmanaged, IPixel { int bytesRead = stream.Read(scratchBuffer, 0, 4); @@ -726,8 +713,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Guard.NotNull(this.tgaMetadata); byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : scratchBuffer[3]; - color.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -745,7 +731,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra16Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private void ReadPalettedBgra16Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = stream.ReadByte(); @@ -754,16 +740,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); } - this.ReadPalettedBgra16Pixel(palette, colorIndex, colorMapPixelSizeInBytes, ref color); - pixelRow[x] = color; + pixelRow[x] = this.ReadPalettedBgra16Pixel(palette, colorIndex, colorMapPixelSizeInBytes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra16Pixel(Span palette, int index, int colorMapPixelSizeInBytes, ref TPixel color) + private TPixel ReadPalettedBgra16Pixel(Span palette, int index, int colorMapPixelSizeInBytes) where TPixel : unmanaged, IPixel { - Bgra5551 bgra = default; - bgra.FromBgra5551(Unsafe.As(ref palette[index * colorMapPixelSizeInBytes])); + Bgra5551 bgra = Unsafe.As(ref palette[index * colorMapPixelSizeInBytes]); if (!this.hasAlpha) { @@ -771,11 +755,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); } - color.FromBgra5551(bgra); + return TPixel.FromBgra5551(bgra); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private static void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = stream.ReadByte(); @@ -784,12 +768,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); } - color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private static void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = stream.ReadByte(); @@ -798,8 +781,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); } - color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); } /// diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index bbb476c01..bb13798c5 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Buffers.Binary; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -160,7 +159,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals private void WriteRunLengthEncodedImage(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { - Rgba32 color = default; Buffer2D pixels = image.PixelBuffer; for (int y = 0; y < image.Height; y++) { @@ -168,14 +166,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int x = 0; x < image.Width;) { TPixel currentPixel = pixelRow[x]; - currentPixel.ToRgba32(ref color); byte equalPixelCount = FindEqualPixels(pixelRow, x); if (equalPixelCount > 0) { // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. stream.WriteByte((byte)(equalPixelCount | 128)); - this.WritePixel(stream, currentPixel, color); + this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); x += equalPixelCount + 1; } else @@ -183,13 +180,12 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals // Write Raw Packet (i.e., Non-Run-Length Encoded): byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x); stream.WriteByte(unEqualPixelCount); - this.WritePixel(stream, currentPixel, color); + this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); x++; for (int i = 0; i < unEqualPixelCount; i++) { currentPixel = pixelRow[x]; - currentPixel.ToRgba32(ref color); - this.WritePixel(stream, currentPixel, color); + this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); x++; } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 1c838b0b7..30a933528 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -102,8 +102,7 @@ internal static class HorizontalPredictor byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); - var rgb = new Rgb24(r, g, b); - rowRgb[x].FromRgb24(rgb); + rowRgb[x] = new Rgb24(r, g, b); } } } @@ -128,8 +127,7 @@ internal static class HorizontalPredictor for (int x = rowL16.Length - 1; x >= 1; x--) { - ushort val = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue); - rowL16[x].PackedValue = val; + rowL16[x].PackedValue = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue); } } } @@ -181,13 +179,13 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort pixelValue = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort pixelValue = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort diff = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort diff = TiffUtilities.ConvertToUShortBigEndian(rowSpan); pixelValue += diff; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue); offset += 2; @@ -200,13 +198,13 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort pixelValue = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort pixelValue = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort diff = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort diff = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); pixelValue += diff; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue); offset += 2; @@ -225,13 +223,13 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint pixelValue = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint pixelValue = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint diff = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint diff = TiffUtilities.ConvertToUIntBigEndian(rowSpan); pixelValue += diff; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, pixelValue); offset += 4; @@ -244,13 +242,13 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint pixelValue = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint pixelValue = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint diff = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint diff = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); pixelValue += diff; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, pixelValue); offset += 4; @@ -278,8 +276,7 @@ internal static class HorizontalPredictor r += pixel.R; g += pixel.G; b += pixel.B; - var rgb = new Rgb24(r, g, b); - pixel.FromRgb24(rgb); + pixel = new Rgb24(r, g, b); } } } @@ -305,8 +302,7 @@ internal static class HorizontalPredictor g += pixel.G; b += pixel.B; a += pixel.A; - var rgb = new Rgba32(r, g, b, a); - pixel.FromRgba32(rgb); + pixel = new Rgba32(r, g, b, a); } } } @@ -321,29 +317,29 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaR = TiffUtilities.ConvertToUShortBigEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaG = TiffUtilities.ConvertToUShortBigEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaB = TiffUtilities.ConvertToUShortBigEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b); offset += 2; @@ -356,29 +352,29 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaR = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaG = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaB = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b); offset += 2; @@ -397,37 +393,37 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort a = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + ushort a = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaR = TiffUtilities.ConvertToUShortBigEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaG = TiffUtilities.ConvertToUShortBigEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaB = TiffUtilities.ConvertToUShortBigEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaA = TiffUtils.ConvertToUShortBigEndian(rowSpan); + ushort deltaA = TiffUtilities.ConvertToUShortBigEndian(rowSpan); a += deltaA; BinaryPrimitives.WriteUInt16BigEndian(rowSpan, a); offset += 2; @@ -440,37 +436,37 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; - ushort a = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + ushort a = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); offset += 2; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 2); - ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaR = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaG = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaB = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b); offset += 2; rowSpan = rowBytes.Slice(offset, 2); - ushort deltaA = TiffUtils.ConvertToUShortLittleEndian(rowSpan); + ushort deltaA = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); a += deltaA; BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, a); offset += 2; @@ -489,29 +485,29 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); offset += 4; @@ -524,29 +520,29 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); offset += 4; @@ -565,37 +561,37 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint a = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + uint a = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaA = TiffUtils.ConvertToUIntBigEndian(rowSpan); + uint deltaA = TiffUtilities.ConvertToUIntBigEndian(rowSpan); a += deltaA; BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a); offset += 4; @@ -608,37 +604,37 @@ internal static class HorizontalPredictor { int offset = 0; Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; - uint a = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + uint a = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); offset += 4; for (int x = 1; x < width; x++) { Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); r += deltaR; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); g += deltaG; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); b += deltaB; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); offset += 4; rowSpan = rowBytes.Slice(offset, 4); - uint deltaA = TiffUtils.ConvertToUIntLittleEndian(rowSpan); + uint deltaA = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); a += deltaA; BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a); offset += 4; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index 8763f9957..2ef261811 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'BlackIsZero' photometric interpretation for 16-bit grayscale images. /// +/// The type of pixel format. internal class BlackIsZero16TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -32,9 +33,8 @@ internal class BlackIsZero16TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - L16 l16 = TiffUtils.L16Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); + L16 l16 = TiffUtilities.L16Default; + TPixel color = TPixel.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) @@ -44,10 +44,10 @@ internal class BlackIsZero16TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ushort intensity = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort intensity = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TPixel.FromL16(new(intensity)); } } else diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 89d1b9d20..c9c0ee581 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -19,11 +19,9 @@ internal class BlackIsZero1TiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { nuint offset = 0; - TPixel colorBlack = default; - TPixel colorWhite = default; + TPixel colorBlack = TPixel.FromRgba32(Color.Black.ToPixel()); + TPixel colorWhite = TPixel.FromRgba32(Color.White.ToPixel()); - colorBlack.FromRgba32(Color.Black.ToPixel()); - colorWhite.FromRgba32(Color.White.ToPixel()); ref byte dataRef = ref MemoryMarshal.GetReference(data); for (nuint y = (uint)top; y < (uint)(top + height); y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index d57130a5f..07bf3d1bd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'BlackIsZero' photometric interpretation for 24-bit grayscale images. /// +/// The type of pixel format. internal class BlackIsZero24TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,8 +25,6 @@ internal class BlackIsZero24TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; @@ -40,10 +38,10 @@ internal class BlackIsZero24TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong intensity = TiffUtils.ConvertToUIntBigEndian(buffer); + uint intensity = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(intensity); } } else @@ -51,10 +49,10 @@ internal class BlackIsZero24TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong intensity = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint intensity = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(intensity); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs index df37327c3..ac316459d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs @@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'BlackIsZero' photometric interpretation for 32-bit float grayscale images. /// +/// The type of pixel format. internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -24,8 +25,6 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default; - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int offset = 0; @@ -41,9 +40,7 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder /// Implements the 'BlackIsZero' photometric interpretation for 32-bit grayscale images. /// +/// The type of pixel format. internal class BlackIsZero32TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,9 +25,6 @@ internal class BlackIsZero32TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - int offset = 0; for (int y = top; y < top + height; y++) { @@ -36,20 +33,20 @@ internal class BlackIsZero32TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong intensity = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint intensity = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(intensity); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong intensity = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint intensity = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(intensity); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs index 16b995441..1d33f1a24 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs @@ -9,47 +9,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for 4-bit grayscale images). /// +/// The type of pixel format. internal class BlackIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; bool isOddWidth = (width & 1) == 1; - var l8 = default(L8); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; - - byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - l8.PackedValue = intensity1; - color.FromL8(l8); - - pixelRowSpan[x++] = color; - - byte intensity2 = (byte)((byteData & 0x0F) * 17); - l8.PackedValue = intensity2; - color.FromL8(l8); - - pixelRowSpan[x++] = color; + pixelRowSpan[x++] = TPixel.FromL8(new((byte)(((byteData & 0xF0) >> 4) * 17))); + pixelRowSpan[x++] = TPixel.FromL8(new((byte)((byteData & 0x0F) * 17))); } if (isOddWidth) { byte byteData = data[offset++]; - - byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - l8.PackedValue = intensity1; - color.FromL8(l8); - - pixelRowSpan[left + width - 1] = color; + pixelRowSpan[left + width - 1] = TPixel.FromL8(new((byte)(((byteData & 0xF0) >> 4) * 17))); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs index b086cb43e..709c2bf64 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,25 +10,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// +/// The type of pixel format. internal class BlackIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; - private readonly float factor; public BlackIsZeroTiffColor(TiffBitsPerSample bitsPerSample) { this.bitsPerSample0 = bitsPerSample.Channel0; - this.factor = (1 << this.bitsPerSample0) - 1.0f; + this.factor = (1 << this.bitsPerSample0) - 1f; } /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - - var bitReader = new BitReader(data); + BitReader bitReader = new(data); for (int y = top; y < top + height; y++) { @@ -38,9 +35,7 @@ internal class BlackIsZeroTiffColor : TiffBaseColorDecoder { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = value / this.factor; - - color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(intensity, intensity, intensity, 1f)); } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs index eb2fe353e..6be584581 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Memory; @@ -13,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements decoding pixel data with photometric interpretation of type 'CieLab' with the planar configuration. /// +/// The type of pixel format. internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -23,11 +23,10 @@ internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - Span l = data[0].GetSpan(); - Span a = data[1].GetSpan(); Span b = data[2].GetSpan(); + Span a = data[1].GetSpan(); + Span l = data[0].GetSpan(); - TPixel color = default; int offset = 0; for (int y = top; y < top + height; y++) { @@ -36,9 +35,7 @@ internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder /// Implements decoding pixel data with photometric interpretation of type 'CieLab'. /// +/// The type of pixel format. internal class CieLabTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private static readonly ColorSpaceConverter ColorSpaceConverter = new(); - - private const float Inv255 = 1.0f / 255.0f; + private const float Inv255 = 1f / 255f; /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default; int offset = 0; for (int y = top; y < top + height; y++) { @@ -33,9 +31,7 @@ internal class CieLabTiffColor : TiffBaseColorDecoder float l = (data[offset] & 0xFF) * 100f * Inv255; CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]); Rgb rgb = ColorSpaceConverter.ToRgb(lab); - - color.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1f)); offset += 3; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs index 2074fb254..77baa5351 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Memory; @@ -12,12 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CmykTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - private const float Inv255 = 1 / 255.0f; + private const float Inv255 = 1f / 255f; /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default; int offset = 0; for (int y = top; y < top + height; y++) { @@ -26,9 +24,7 @@ internal class CmykTiffColor : TiffBaseColorDecoder { Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255); Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk); - - color.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f)); offset += 4; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index 22db1918c..745e5846a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// +/// The type of pixel format. internal class PaletteTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -18,8 +18,11 @@ internal class PaletteTiffColor : TiffBaseColorDecoder private readonly TPixel[] palette; - private const float InvMax = 1.0f / 65535F; + private const float InvMax = 1f / 65535f; + /// + /// Initializes a new instance of the class. + /// /// The number of bits per sample for each pixel. /// The RGB color lookup table to use for decoding the image. public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) @@ -32,7 +35,7 @@ internal class PaletteTiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var bitReader = new BitReader(data); + BitReader bitReader = new(data); for (int y = top; y < top + height; y++) { @@ -49,7 +52,7 @@ internal class PaletteTiffColor : TiffBaseColorDecoder private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) { - var palette = new TPixel[colorCount]; + TPixel[] palette = new TPixel[colorCount]; const int rOffset = 0; int gOffset = colorCount; @@ -60,7 +63,7 @@ internal class PaletteTiffColor : TiffBaseColorDecoder float r = colorMap[rOffset + i] * InvMax; float g = colorMap[gOffset + i] * InvMax; float b = colorMap[bOffset + i] * InvMax; - palette[i].FromScaledVector4(new Vector4(r, g, b, 1.0f)); + palette[i] = TPixel.FromScaledVector4(new(r, g, b, 1f)); } return palette; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 8ca45c939..d8520e307 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,11 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 16 bits for each channel. /// +/// The type of pixel format. internal class Rgb161616TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; - private readonly Configuration configuration; /// @@ -32,10 +31,6 @@ internal class Rgb161616TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - Rgba64 rgba = TiffUtils.Rgba64Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - int offset = 0; for (int y = top; y < top + height; y++) @@ -46,14 +41,14 @@ internal class Rgb161616TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); + pixelRow[x] = TPixel.FromRgb48(new(r, g, b)); } } else diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 08fb6d8be..f55d7225e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 16 bit. /// +/// The type of pixel format. internal class Rgb16PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -26,10 +26,6 @@ internal class Rgb16PlanarTiffColor : TiffBasePlanarColorDecoder /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - Rgba64 rgba = TiffUtils.Rgba64Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); Span blueData = data[2].GetSpan(); @@ -42,26 +38,26 @@ internal class Rgb16PlanarTiffColor : TiffBasePlanarColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2)); - ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2)); - ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortBigEndian(redData.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortBigEndian(greenData.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortBigEndian(blueData.Slice(offset, 2)); offset += 2; - pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); + pixelRow[x] = TPixel.FromRgb48(new(r, g, b)); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); - ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); - ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); offset += 2; - pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); + pixelRow[x] = TPixel.FromRgb48(new(r, g, b)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs index 027dcce3b..074c08530 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 24 bits for each channel. /// +/// The type of pixel format. internal class Rgb242424TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,8 +25,6 @@ internal class Rgb242424TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); int offset = 0; Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; @@ -41,18 +39,18 @@ internal class Rgb242424TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); + uint r = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); + uint g = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); + uint b = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(r, g, b); } } else @@ -60,18 +58,18 @@ internal class Rgb242424TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(r, g, b); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs index eba29a7f7..03ee94c27 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 24 bit. /// +/// The type of pixel format. internal class Rgb24PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -26,8 +26,6 @@ internal class Rgb24PlanarTiffColor : TiffBasePlanarColorDecoder /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; @@ -45,15 +43,15 @@ internal class Rgb24PlanarTiffColor : TiffBasePlanarColorDecoder for (int x = 0; x < pixelRow.Length; x++) { redData.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); + uint r = TiffUtilities.ConvertToUIntBigEndian(buffer); greenData.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); + uint g = TiffUtilities.ConvertToUIntBigEndian(buffer); blueData.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); + uint b = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(r, g, b); } } else @@ -61,15 +59,15 @@ internal class Rgb24PlanarTiffColor : TiffBasePlanarColorDecoder for (int x = 0; x < pixelRow.Length; x++) { redData.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer); greenData.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer); blueData.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(r, g, b); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs index 79f66c143..5f0497259 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 32 bits for each channel. /// +/// The type of pixel format. internal class Rgb323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,8 +25,6 @@ internal class Rgb323232TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) @@ -37,32 +35,32 @@ internal class Rgb323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(r, g, b); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(r, g, b); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs index 472697dd5..caa6eb51d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 32 bit. /// +/// The type of pixel format. internal class Rgb32PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -26,9 +26,6 @@ internal class Rgb32PlanarTiffColor : TiffBasePlanarColorDecoder /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); Span blueData = data[2].GetSpan(); @@ -41,26 +38,26 @@ internal class Rgb32PlanarTiffColor : TiffBasePlanarColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntBigEndian(redData.Slice(offset, 4)); - ulong g = TiffUtils.ConvertToUIntBigEndian(greenData.Slice(offset, 4)); - ulong b = TiffUtils.ConvertToUIntBigEndian(blueData.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntBigEndian(redData.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntBigEndian(greenData.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntBigEndian(blueData.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(r, g, b); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); - ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); - ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(r, g, b); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs index 7c6a4a0ec..3a90e8174 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs @@ -9,17 +9,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation for 4 bits per color channel images. /// +/// The type of pixel format. internal class Rgb444TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; - var bgra = default(Bgra4444); for (int y = top; y < top + height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y); @@ -31,9 +29,8 @@ internal class Rgb444TiffColor : TiffBaseColorDecoder offset++; byte b = (byte)((data[offset] & 0xF0) >> 4); - bgra.PackedValue = ToBgraPackedValue(b, g, r); - color.FromScaledVector4(bgra.ToScaledVector4()); - pixelRow[x] = color; + Bgra4444 bgra = new() { PackedValue = ToBgraPackedValue(b, g, r) }; + pixelRow[x] = TPixel.FromScaledVector4(bgra.ToScaledVector4()); if (x + 1 >= pixelRow.Length) { offset++; @@ -47,8 +44,7 @@ internal class Rgb444TiffColor : TiffBaseColorDecoder offset++; bgra.PackedValue = ToBgraPackedValue(b, g, r); - color.FromScaledVector4(bgra.ToScaledVector4()); - pixelRow[x + 1] = color; + pixelRow[x + 1] = TPixel.FromScaledVector4(bgra.ToScaledVector4()); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs index 1c3af5562..37605ef80 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,6 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 32 bits for each channel. /// +/// The type of pixel format. internal class RgbFloat323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -24,8 +24,6 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); int offset = 0; Span buffer = stackalloc byte[4]; @@ -52,9 +50,7 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder float b = BitConverter.ToSingle(buffer); offset += 4; - var colorVector = new Vector4(r, g, b, 1.0f); - color.FromScaledVector4(colorVector); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f)); } } else @@ -70,9 +66,7 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder float b = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - var colorVector = new Vector4(r, g, b, 1.0f); - color.FromScaledVector4(colorVector); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index 0b822f5a0..844b08d3c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// +/// The type of pixel format. internal class RgbPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -49,11 +49,9 @@ internal class RgbPlanarTiffColor : TiffBasePlanarColorDecoder /// The height of the image block. public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - - var rBitReader = new BitReader(data[0].GetSpan()); - var gBitReader = new BitReader(data[1].GetSpan()); - var bBitReader = new BitReader(data[2].GetSpan()); + BitReader rBitReader = new(data[0].GetSpan()); + BitReader gBitReader = new(data[1].GetSpan()); + BitReader bBitReader = new(data[2].GetSpan()); for (int y = top; y < top + height; y++) { @@ -64,8 +62,7 @@ internal class RgbPlanarTiffColor : TiffBasePlanarColorDecoder float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; - color.FromScaledVector4(new Vector4(r, g, b, 1.0f)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f)); } rBitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs index dcaab94a6..3c205d147 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation (for all bit depths). /// +/// The type of pixel format. internal class RgbTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -40,9 +41,7 @@ internal class RgbTiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - - var bitReader = new BitReader(data); + BitReader bitReader = new(data); for (int y = top; y < top + height; y++) { @@ -53,8 +52,7 @@ internal class RgbTiffColor : TiffBaseColorDecoder float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; - color.FromScaledVector4(new Vector4(r, g, b, 1.0f)); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f)); } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs index 0467f7ad5..e4965887d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs @@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel. /// +/// The type of pixel format. internal class Rgba16161616TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -29,8 +30,8 @@ internal class Rgba16161616TiffColor : TiffBaseColorDecoder /// /// The configuration. /// The memory allocator. - /// if set to true decodes the pixel data as big endian, otherwise as little endian. /// The type of the extra samples. + /// if set to true decodes the pixel data as big endian, otherwise as little endian. public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.configuration = configuration; @@ -42,15 +43,11 @@ internal class Rgba16161616TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - Rgba64 rgba = TiffUtils.Rgba64Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null; - Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty; + Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : []; for (int y = top; y < top + height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); @@ -59,18 +56,18 @@ internal class Rgba16161616TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); + ushort a = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); offset += 2; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : - TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorFromRgba64Premultiplied(r, g, b, a) + : TPixel.FromRgba64(new(r, g, b, a)); } } else diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs index 7426544d2..3d36db17d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit. /// +/// The type of pixel format. internal class Rgba16PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -33,10 +33,6 @@ internal class Rgba16PlanarTiffColor : TiffBasePlanarColorDecoder public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - Rgba64 rgba = TiffUtils.Rgba64Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); Span blueData = data[2].GetSpan(); @@ -51,32 +47,32 @@ internal class Rgba16PlanarTiffColor : TiffBasePlanarColorDecoder(r, g, b, a) + : TPixel.FromRgba64(new(r, g, b, a)); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); - ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); - ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); - ulong a = TiffUtils.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2)); + ushort r = TiffUtilities.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); + ushort g = TiffUtilities.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); + ushort b = TiffUtilities.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); + ushort a = TiffUtilities.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2)); offset += 2; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : - TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorFromRgba64Premultiplied(r, g, b, a) + : TPixel.FromRgba64(new(r, g, b, a)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs index eba60679a..a29469365 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel. /// +/// The type of pixel format. internal class Rgba24242424TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -32,9 +32,6 @@ internal class Rgba24242424TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; @@ -51,24 +48,24 @@ internal class Rgba24242424TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); + uint r = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); + uint g = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); + uint b = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong a = TiffUtils.ConvertToUIntBigEndian(buffer); + uint a = TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : - TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorScaleTo24BitPremultiplied(r, g, b, a) + : TiffUtilities.ColorScaleTo24Bit(r, g, b, a); } } else @@ -76,24 +73,24 @@ internal class Rgba24242424TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; data.Slice(offset, 3).CopyTo(bufferSpan); - ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer); + uint a = TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : - TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorScaleTo24BitPremultiplied(r, g, b, a) + : TiffUtilities.ColorScaleTo24Bit(r, g, b, a); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs index 1b842a79b..222e72986 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit. /// +/// The type of pixel format. internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -33,8 +33,6 @@ internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; @@ -54,19 +52,19 @@ internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder(r, g, b, a) + : TiffUtilities.ColorScaleTo24Bit(r, g, b, a); } } else @@ -74,19 +72,19 @@ internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder(r, g, b, a) + : TiffUtilities.ColorScaleTo24Bit(r, g, b, a); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs index 2193f2e81..5c57221d9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel. /// +/// The type of pixel format. internal class Rgba32323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -32,9 +32,6 @@ internal class Rgba32323232TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; @@ -46,42 +43,42 @@ internal class Rgba32323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint a = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : - TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorScaleTo32BitPremultiplied(r, g, b, a) + : TiffUtilities.ColorScaleTo32Bit(r, g, b, a); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint a = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : - TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorScaleTo32BitPremultiplied(r, g, b, a) + : TiffUtilities.ColorScaleTo32Bit(r, g, b, a); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs index 7d047cf7f..8f9090741 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and a 'Planar' layout for each color channel with 32 bit. /// +/// The type of pixel format. internal class Rgba32PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; - private readonly TiffExtraSampleType? extraSamplesType; /// @@ -33,9 +32,6 @@ internal class Rgba32PlanarTiffColor : TiffBasePlanarColorDecoder public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); Span blueData = data[2].GetSpan(); @@ -50,32 +46,32 @@ internal class Rgba32PlanarTiffColor : TiffBasePlanarColorDecoder(r, g, b, a) + : TiffUtilities.ColorScaleTo32Bit(r, g, b, a); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); - ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); - ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); - ulong a = TiffUtils.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4)); + uint r = TiffUtilities.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); + uint g = TiffUtilities.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); + uint b = TiffUtilities.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); + uint a = TiffUtilities.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4)); offset += 4; - pixelRow[x] = hasAssociatedAlpha ? - TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : - TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); + pixelRow[x] = hasAssociatedAlpha + ? TiffUtilities.ColorScaleTo32BitPremultiplied(r, g, b, a) + : TiffUtilities.ColorScaleTo32Bit(r, g, b, a); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs index dc1fbb871..26ffbbab9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs @@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and 8 bits per channel. /// +/// The type of pixel format. internal class Rgba8888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -34,10 +35,8 @@ internal class Rgba8888TiffColor : TiffBaseColorDecoder int offset = 0; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null; - Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty; + Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : []; for (int y = top; y < top + height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index 743502d56..12f1b75b4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,6 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel. /// +/// The type of pixel format. internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -24,8 +24,6 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); int offset = 0; Span buffer = stackalloc byte[4]; @@ -57,9 +55,7 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder float a = BitConverter.ToSingle(buffer); offset += 4; - var colorVector = new Vector4(r, g, b, a); - color.FromScaledVector4(colorVector); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, a)); } } else @@ -78,9 +74,7 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder float a = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - var colorVector = new Vector4(r, g, b, a); - color.FromScaledVector4(colorVector); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, a)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs index 63aa64d86..7a599a06a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs @@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout (for all bit depths). /// +/// The type of pixel format. internal class RgbaPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { @@ -59,13 +60,12 @@ internal class RgbaPlanarTiffColor : TiffBasePlanarColorDecoder /// The height of the image block. public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); bool hasAssociatedAlpha = this.extraSampleType.HasValue && this.extraSampleType == TiffExtraSampleType.AssociatedAlphaData; - var rBitReader = new BitReader(data[0].GetSpan()); - var gBitReader = new BitReader(data[1].GetSpan()); - var bBitReader = new BitReader(data[2].GetSpan()); - var aBitReader = new BitReader(data[3].GetSpan()); + BitReader rBitReader = new(data[0].GetSpan()); + BitReader gBitReader = new(data[1].GetSpan()); + BitReader bBitReader = new(data[2].GetSpan()); + BitReader aBitReader = new(data[3].GetSpan()); for (int y = top; y < top + height; y++) { @@ -77,17 +77,15 @@ internal class RgbaPlanarTiffColor : TiffBasePlanarColorDecoder float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor; - var vec = new Vector4(r, g, b, a); + Vector4 vector = new(r, g, b, a); if (hasAssociatedAlpha) { - color = TiffUtils.UnPremultiply(ref vec, color); + pixelRow[x] = TiffUtilities.UnPremultiply(ref vector); } else { - color.FromScaledVector4(vec); + pixelRow[x] = TPixel.FromScaledVector4(vector); } - - pixelRow[x] = color; } rBitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs index 0ea8a87fc..68b59c95a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'RGB' photometric interpretation with alpha channel (for all bit depths). /// +/// The type of pixel format. internal class RgbaTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -50,9 +51,7 @@ internal class RgbaTiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - - var bitReader = new BitReader(data); + BitReader bitReader = new(data); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; @@ -66,15 +65,14 @@ internal class RgbaTiffColor : TiffBaseColorDecoder float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor; - var vec = new Vector4(r, g, b, a); + Vector4 vector = new(r, g, b, a); if (hasAssociatedAlpha) { - pixelRow[x] = TiffUtils.UnPremultiply(ref vec, color); + pixelRow[x] = TiffUtilities.UnPremultiply(ref vector); } else { - color.FromScaledVector4(vec); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromScaledVector4(vector); } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index f7fd55e52..6f1672e1b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation for 16-bit grayscale images. /// +/// The type of pixel format. internal class WhiteIsZero16TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,10 +25,6 @@ internal class WhiteIsZero16TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - L16 l16 = TiffUtils.L16Default; - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); - int offset = 0; for (int y = top; y < top + height; y++) { @@ -37,20 +33,20 @@ internal class WhiteIsZero16TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2))); + ushort intensity = (ushort)(ushort.MaxValue - TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2))); offset += 2; - pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TPixel.FromL16(new(intensity)); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2))); + ushort intensity = (ushort)(ushort.MaxValue - TiffUtilities.ConvertToUShortLittleEndian(data.Slice(offset, 2))); offset += 2; - pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TPixel.FromL16(new(intensity)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index 4cba8f2d7..1de9c295b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for bilevel images). /// +/// The type of pixel format. internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -18,11 +19,9 @@ internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { nuint offset = 0; - var colorBlack = default(TPixel); - var colorWhite = default(TPixel); + TPixel colorBlack = TPixel.FromRgba32(Color.Black.ToPixel()); + TPixel colorWhite = TPixel.FromRgba32(Color.White.ToPixel()); - colorBlack.FromRgba32(Color.Black.ToPixel()); - colorWhite.FromRgba32(Color.White.ToPixel()); ref byte dataRef = ref MemoryMarshal.GetReference(data); for (nuint y = (uint)top; y < (uint)(top + height); y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index 59e0df87d..94549d663 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation for 24-bit grayscale images. /// +/// The type of pixel format. internal class WhiteIsZero24TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,8 +25,6 @@ internal class WhiteIsZero24TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; const uint maxValue = 0xFFFFFF; @@ -41,10 +39,10 @@ internal class WhiteIsZero24TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(buffer); + uint intensity = maxValue - TiffUtilities.ConvertToUIntBigEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(intensity); } } else @@ -52,10 +50,10 @@ internal class WhiteIsZero24TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 3).CopyTo(bufferSpan); - ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(buffer); + uint intensity = maxValue - TiffUtilities.ConvertToUIntLittleEndian(buffer); offset += 3; - pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo24Bit(intensity); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index f3207b2f4..7d31f23ab 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images. /// +/// The type of pixel format. internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -24,8 +25,6 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int offset = 0; @@ -41,9 +40,7 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder /// Implements the 'WhiteIsZero' photometric interpretation for 32-bit grayscale images. /// +/// The type of pixel format. internal class WhiteIsZero32TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -25,8 +25,6 @@ internal class WhiteIsZero32TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - color.FromScaledVector4(Vector4.Zero); const uint maxValue = 0xFFFFFFFF; int offset = 0; @@ -37,20 +35,20 @@ internal class WhiteIsZero32TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); + uint intensity = maxValue - TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(intensity); } } else { for (int x = 0; x < pixelRow.Length; x++) { - ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); + uint intensity = maxValue - TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4)); offset += 4; - pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); + pixelRow[x] = TiffUtilities.ColorScaleTo32Bit(intensity); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs index bc5e2fb64..7dcbe23c5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs @@ -9,47 +9,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 4-bit grayscale images). /// +/// The type of pixel format. internal class WhiteIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; bool isOddWidth = (width & 1) == 1; - var l8 = default(L8); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; - - byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - l8.PackedValue = intensity1; - color.FromL8(l8); - - pixelRowSpan[x++] = color; - - byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); - l8.PackedValue = intensity2; - color.FromL8(l8); - - pixelRowSpan[x++] = color; + pixelRowSpan[x++] = TPixel.FromL8(new((byte)((15 - ((byteData & 0xF0) >> 4)) * 17))); + pixelRowSpan[x++] = TPixel.FromL8(new((byte)((15 - (byteData & 0x0F)) * 17))); } if (isOddWidth) { byte byteData = data[offset++]; - - byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - l8.PackedValue = intensity1; - color.FromL8(l8); - - pixelRowSpan[left + width - 1] = color; + pixelRowSpan[left + width - 1] = TPixel.FromL8(new((byte)((15 - ((byteData & 0xF0) >> 4)) * 17))); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs index fb2653543..5429dbd3b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,24 +9,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 8-bit grayscale images). /// +/// The type of pixel format. internal class WhiteIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; - - var l8 = default(L8); for (int y = top; y < top + height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { byte intensity = (byte)(byte.MaxValue - data[offset++]); - pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); + pixelRow[x] = TPixel.FromL8(new(intensity)); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs index b38868a0e..0cd01a619 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs @@ -11,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// +/// The type of pixel format. internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; - private readonly float factor; public WhiteIsZeroTiffColor(TiffBitsPerSample bitsPerSample) @@ -27,9 +27,7 @@ internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - - var bitReader = new BitReader(data); + BitReader bitReader = new(data); for (int y = top; y < top + height; y++) { @@ -37,10 +35,8 @@ internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { int value = bitReader.ReadBits(this.bitsPerSample0); - float intensity = 1.0f - (value / this.factor); - - color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f)); - pixelRow[x] = color; + float intensity = 1f - (value / this.factor); + pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f)); } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs index 791bfa438..768177bfc 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs @@ -11,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements decoding pixel data with photometric interpretation of type 'YCbCr' with the planar configuration. /// +/// The type of pixel format. internal class YCbCrPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly YCbCrConverter converter; - private readonly ushort[] ycbcrSubSampling; public YCbCrPlanarTiffColor(Rational[] referenceBlackAndWhite, Rational[] coefficients, ushort[] ycbcrSubSampling) @@ -36,13 +36,12 @@ internal class YCbCrPlanarTiffColor : TiffBasePlanarColorDecoder ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData); } - var color = default(TPixel); int offset = 0; int widthPadding = 0; if (this.ycbcrSubSampling != null) { // Round to the next integer multiple of horizontalSubSampling. - widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); + widthPadding = TiffUtilities.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); } for (int y = top; y < top + height; y++) @@ -51,8 +50,7 @@ internal class YCbCrPlanarTiffColor : TiffBasePlanarColorDecoder for (int x = 0; x < pixelRow.Length; x++) { Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]); - color.FromRgba32(rgba); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromRgba32(rgba); offset++; } @@ -64,8 +62,8 @@ internal class YCbCrPlanarTiffColor : TiffBasePlanarColorDecoder { // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // then the source data will be padded. - width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); - height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); + width += TiffUtilities.PaddingToNextInteger(width, horizontalSubSampling); + height += TiffUtilities.PaddingToNextInteger(height, verticalSubSampling); for (int row = height - 1; row >= 0; row--) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs index 2e47698a6..5a1389035 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; /// /// Implements decoding pixel data with photometric interpretation of type 'YCbCr'. /// +/// The type of pixel format. internal class YCbCrTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { @@ -50,13 +51,12 @@ internal class YCbCrTiffColor : TiffBaseColorDecoder private void DecodeYCbCrData(Buffer2D pixels, int left, int top, int width, int height, ReadOnlySpan ycbcrData) { - var color = default(TPixel); int offset = 0; int widthPadding = 0; if (this.ycbcrSubSampling != null) { // Round to the next integer multiple of horizontalSubSampling. - widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); + widthPadding = TiffUtilities.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); } for (int y = top; y < top + height; y++) @@ -65,8 +65,7 @@ internal class YCbCrTiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { Rgba32 rgba = this.converter.ConvertToRgba32(ycbcrData[offset], ycbcrData[offset + 1], ycbcrData[offset + 2]); - color.FromRgba32(rgba); - pixelRow[x] = color; + pixelRow[x] = TPixel.FromRgba32(rgba); offset += 3; } @@ -78,8 +77,8 @@ internal class YCbCrTiffColor : TiffBaseColorDecoder { // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // then the source data will be padded. - width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); - height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); + width += TiffUtilities.PaddingToNextInteger(width, horizontalSubSampling); + height += TiffUtilities.PaddingToNextInteger(height, verticalSubSampling); int blockWidth = width / horizontalSubSampling; int blockHeight = height / verticalSubSampling; int cbCrOffsetInBlock = horizontalSubSampling * verticalSubSampling; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index aed6d4ec6..63dc62399 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -163,8 +163,8 @@ internal class TiffDecoderCore : IImageDecoderInternals public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - List> frames = new(); - List framesMetadata = new(); + List> frames = []; + List framesMetadata = []; try { this.inputStream = stream; @@ -221,7 +221,7 @@ internal class TiffDecoderCore : IImageDecoderInternals DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); IList directories = reader.Read(); - List framesMetadata = new(); + List framesMetadata = []; foreach (ExifProfile dir in directories) { framesMetadata.Add(this.CreateFrameMetadata(dir)); diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs new file mode 100644 index 000000000..8b5e04f27 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs @@ -0,0 +1,120 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.Utils; + +/// +/// Helper methods for TIFF decoding. +/// +internal static class TiffUtilities +{ + private const float Scale24Bit = 1f / 0xFFFFFF; + private static readonly Vector4 Scale24BitVector = Vector128.Create(Scale24Bit, Scale24Bit, Scale24Bit, 1f).AsVector4(); + + private const float Scale32Bit = 1f / 0xFFFFFFFF; + private static readonly Vector4 Scale32BitVector = Vector128.Create(Scale32Bit, Scale32Bit, Scale32Bit, 1f).AsVector4(); + + public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0); + + public static L16 L16Default { get; } = new(0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ConvertToUShortBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ConvertToUShortLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ConvertToUIntBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32BigEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ConvertToUIntLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32LittleEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorFromRgba64Premultiplied(ushort r, ushort g, ushort b, ushort a) + where TPixel : unmanaged, IPixel + { + if (a == 0) + { + return TPixel.FromRgba64(default); + } + + return TPixel.FromRgba64(new((ushort)(r / a), (ushort)(g / a), (ushort)(b / a), a)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo24Bit(uint r, uint g, uint b) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(r, g, b, 1f) * Scale24BitVector); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo24Bit(uint r, uint g, uint b, uint a) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(r, g, b, a) * Scale24Bit); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo24BitPremultiplied(uint r, uint g, uint b, uint a) + where TPixel : unmanaged, IPixel + { + Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit; + return UnPremultiply(ref colorVector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo32Bit(uint r, uint g, uint b) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(r, g, b, 1f) * Scale32BitVector); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo32Bit(uint r, uint g, uint b, uint a) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(r, g, b, a) * Scale32Bit); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo32BitPremultiplied(uint r, uint g, uint b, uint a) + where TPixel : unmanaged, IPixel + { + Vector4 vector = new Vector4(r, g, b, a) * Scale32Bit; + return UnPremultiply(ref vector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo24Bit(uint intensity) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale24BitVector); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorScaleTo32Bit(uint intensity) + where TPixel : unmanaged, IPixel + => TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale32BitVector); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel UnPremultiply(ref Vector4 vector) + where TPixel : unmanaged, IPixel + { + Numerics.UnPremultiply(ref vector); + return TPixel.FromScaledVector4(vector); + } + + /// + /// Finds the padding needed to round 'valueToRoundUp' to the next integer multiple of subSampling value. + /// + /// The width or height to round up. + /// The sub sampling. + /// The padding. + public static int PaddingToNextInteger(int valueToRoundUp, int subSampling) + { + if (valueToRoundUp % subSampling == 0) + { + return 0; + } + + return subSampling - (valueToRoundUp % subSampling); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs deleted file mode 100644 index 7e0251af6..000000000 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Tiff.Utils; - -/// -/// Helper methods for TIFF decoding. -/// -internal static class TiffUtils -{ - private const float Scale24Bit = 1.0f / 0xFFFFFF; - - private const float Scale32Bit = 1.0f / 0xFFFFFFFF; - - public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0); - - public static L16 L16Default { get; } = new(0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort ConvertToUShortBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort ConvertToUShortLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint ConvertToUIntBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32BigEndian(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint ConvertToUIntLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32LittleEndian(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorFromL8(L8 l8, byte intensity, TPixel color) - where TPixel : unmanaged, IPixel - { - l8.PackedValue = intensity; - color.FromL8(l8); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorFromRgb64(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color) - where TPixel : unmanaged, IPixel - { - rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); - color.FromRgba64(rgba); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorFromRgba64(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48); - color.FromRgba64(rgba); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorFromRgba64Premultiplied(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48); - var vec = rgba.ToVector4(); - return UnPremultiply(ref vec, color); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, TPixel color) - where TPixel : unmanaged, IPixel - { - var colorVector = new Vector4(r * Scale24Bit, g * Scale24Bit, b * Scale24Bit, 1.0f); - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit; - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo24BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit; - return UnPremultiply(ref colorVector, color); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, TPixel color) - where TPixel : unmanaged, IPixel - { - var colorVector = new Vector4(r * Scale32Bit, g * Scale32Bit, b * Scale32Bit, 1.0f); - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit; - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo32BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color) - where TPixel : unmanaged, IPixel - { - Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit; - return UnPremultiply(ref colorVector, color); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color) - where TPixel : unmanaged, IPixel - { - l16.PackedValue = intensity; - color.FromL16(l16); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo24Bit(ulong intensity, TPixel color) - where TPixel : unmanaged, IPixel - { - var colorVector = new Vector4(intensity * Scale24Bit, intensity * Scale24Bit, intensity * Scale24Bit, 1.0f); - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ColorScaleTo32Bit(ulong intensity, TPixel color) - where TPixel : unmanaged, IPixel - { - var colorVector = new Vector4(intensity * Scale32Bit, intensity * Scale32Bit, intensity * Scale32Bit, 1.0f); - color.FromScaledVector4(colorVector); - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel UnPremultiply(ref Vector4 vector, TPixel color) - where TPixel : unmanaged, IPixel - { - Numerics.UnPremultiply(ref vector); - color.FromScaledVector4(vector); - - return color; - } - - /// - /// Finds the padding needed to round 'valueToRoundUp' to the next integer multiple of subSampling value. - /// - /// The width or height to round up. - /// The sub sampling. - /// The padding. - public static int PaddingToNextInteger(int valueToRoundUp, int subSampling) - { - if (valueToRoundUp % subSampling == 0) - { - return 0; - } - - return subSampling - (valueToRoundUp % subSampling); - } -} diff --git a/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs b/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs index 3eb03b172..65d5b65e8 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs @@ -140,7 +140,6 @@ internal sealed class WebpLossyDecoder private static void DecodePixelValues(int width, int height, Span pixelData, Buffer2D decodedPixels, IMemoryOwner alpha) where TPixel : unmanaged, IPixel { - TPixel color = default; Span alphaSpan = alpha.Memory.Span; Span pixelsBgr = MemoryMarshal.Cast(pixelData); for (int y = 0; y < height; y++) @@ -151,8 +150,7 @@ internal sealed class WebpLossyDecoder { int offset = yMulWidth + x; Bgr24 bgr = pixelsBgr[offset]; - color.FromBgra32(new Bgra32(bgr.R, bgr.G, bgr.B, alphaSpan[offset])); - decodedPixelRow[x] = color; + decodedPixelRow[x] = TPixel.FromBgra32(new(bgr.R, bgr.G, bgr.B, alphaSpan[offset])); } } } diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index f9d88deac..f473ce248 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -43,96 +43,96 @@ public interface IPixel : IPixel, IEquatable /// The . static abstract TSelf FromVector4(Vector4 source); + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + /// The . + static abstract TSelf FromAbgr32(Abgr32 source); + /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromArgb32(Argb32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromArgb32(Argb32 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromBgra5551(Bgra5551 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromBgra5551(Bgra5551 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromBgr24(Bgr24 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromBgr24(Bgr24 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromBgra32(Bgra32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); - - /// - /// Initializes the pixel instance from an value. - /// - /// The value. - /// The . - static virtual TSelf FromAbgr32(Abgr32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromBgra32(Bgra32 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromL8(L8 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromL8(L8 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromL16(L16 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromL16(L16 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromLa16(La16 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromLa16(La16 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromLa32(La32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromLa32(La32 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromRgb24(Rgb24 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromRgb24(Rgb24 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromRgba32(Rgba32 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromRgba32(Rgba32 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromRgb48(Rgb48 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromRgb48(Rgb48 source); /// /// Initializes the pixel instance from an value. /// /// The value. /// The . - static virtual TSelf FromRgba64(Rgba64 source) => TSelf.FromScaledVector4(source.ToScaledVector4()); + static abstract TSelf FromRgba64(Rgba64 source); #pragma warning restore CA1000 // Do not declare static members on generic types } @@ -145,7 +145,7 @@ public interface IPixel /// Convert the pixel instance into representation. /// /// The - virtual Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToVector4()); + Rgba32 ToRgba32(); /// /// Expands the pixel into a generic ("scaled") representation diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 079230676..f62d3c676 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -38,9 +38,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -143,9 +141,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -248,9 +244,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -353,9 +347,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -458,9 +450,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -563,9 +553,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -668,9 +656,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -773,9 +759,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -878,9 +862,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -983,9 +965,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1088,9 +1068,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1193,9 +1171,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1298,9 +1274,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1403,9 +1377,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1508,9 +1480,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1613,9 +1583,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1718,9 +1686,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1823,9 +1789,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1928,9 +1892,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2033,9 +1995,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2138,9 +2098,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2243,9 +2201,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2348,9 +2304,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2453,9 +2407,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2558,9 +2510,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2663,9 +2613,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2768,9 +2716,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2873,9 +2819,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2978,9 +2922,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3083,9 +3025,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3188,9 +3128,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3293,9 +3231,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3398,9 +3334,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3503,9 +3437,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3608,9 +3540,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3713,9 +3643,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3818,9 +3746,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3923,9 +3849,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4028,9 +3952,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4133,9 +4055,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4238,9 +4158,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4343,9 +4261,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4448,9 +4364,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4553,9 +4467,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4658,9 +4570,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4763,9 +4673,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4868,9 +4776,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4973,9 +4879,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5078,9 +4982,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5183,9 +5085,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5288,9 +5188,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5393,9 +5291,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5498,9 +5394,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5603,9 +5497,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5708,9 +5600,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5813,9 +5703,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5918,9 +5806,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6023,9 +5909,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6128,9 +6012,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6233,9 +6115,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6338,9 +6218,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6443,9 +6321,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6548,9 +6424,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6653,9 +6527,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6758,9 +6630,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6863,9 +6733,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6968,9 +6836,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7073,9 +6939,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7178,9 +7042,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7283,9 +7145,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7388,9 +7248,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7493,9 +7351,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7598,9 +7454,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7703,9 +7557,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7808,9 +7660,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7913,9 +7763,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8018,9 +7866,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8123,9 +7969,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8228,9 +8072,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8333,9 +8175,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8438,9 +8278,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8543,9 +8381,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8648,9 +8484,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8753,9 +8587,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8858,9 +8690,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8963,9 +8793,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9068,9 +8896,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9173,9 +8999,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9278,9 +9102,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9383,9 +9205,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9488,9 +9308,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9593,9 +9411,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9698,9 +9514,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9803,9 +9617,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9908,9 +9720,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10013,9 +9823,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10118,9 +9926,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10223,9 +10029,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10328,9 +10132,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10433,9 +10235,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10538,9 +10338,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10643,9 +10441,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10748,9 +10544,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10853,9 +10647,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10958,9 +10750,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -11063,9 +10853,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -11168,9 +10956,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -11273,9 +11059,7 @@ internal static class DefaultPixelBlenders /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index da6208eaa..5b71bb0a6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -81,9 +81,7 @@ var blenders = new []{ /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - return dest; + return TPixel.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index e7cf3b292..255bafc79 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -355,9 +355,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -373,9 +371,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -391,9 +387,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -409,9 +403,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -427,9 +419,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -445,9 +435,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -463,9 +451,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -481,9 +467,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -499,9 +483,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -517,9 +499,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -535,9 +515,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -553,9 +531,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -900,9 +876,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -918,9 +892,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -936,9 +908,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -954,9 +924,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -972,9 +940,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -990,9 +956,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1008,9 +972,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1026,9 +988,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1044,9 +1004,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1062,9 +1020,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1080,9 +1036,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1098,9 +1052,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1445,9 +1397,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1463,9 +1413,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1481,9 +1429,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1499,9 +1445,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1517,9 +1461,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1535,9 +1477,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1553,9 +1493,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1571,9 +1509,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1589,9 +1525,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1607,9 +1541,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1625,9 +1557,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1643,9 +1573,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -1990,9 +1918,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2008,9 +1934,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2026,9 +1950,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2044,9 +1966,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2062,9 +1982,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2080,9 +1998,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2098,9 +2014,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2116,9 +2030,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2134,9 +2046,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2152,9 +2062,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2170,9 +2078,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2188,9 +2094,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2535,9 +2439,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2553,9 +2455,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2571,9 +2471,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2589,9 +2487,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2607,9 +2503,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2625,9 +2519,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2643,9 +2535,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2661,9 +2551,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2679,9 +2567,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2697,9 +2583,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2715,9 +2599,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -2733,9 +2615,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3080,9 +2960,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3098,9 +2976,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3116,9 +2992,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3134,9 +3008,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3152,9 +3024,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3170,9 +3040,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3188,9 +3056,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3206,9 +3072,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3224,9 +3088,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3242,9 +3104,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3260,9 +3120,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3278,9 +3136,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3625,9 +3481,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3643,9 +3497,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3661,9 +3513,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3679,9 +3529,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3697,9 +3545,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3715,9 +3561,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3733,9 +3577,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3751,9 +3593,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3769,9 +3609,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3787,9 +3625,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3805,9 +3641,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -3823,9 +3657,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4170,9 +4002,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4188,9 +4018,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4206,9 +4034,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4224,9 +4050,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4242,9 +4066,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4260,9 +4082,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4278,9 +4098,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4296,9 +4114,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4314,9 +4130,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4332,9 +4146,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4350,9 +4162,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4368,9 +4178,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4715,9 +4523,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4733,9 +4539,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4751,9 +4555,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4769,9 +4571,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4787,9 +4587,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4805,9 +4603,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4823,9 +4619,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4841,9 +4635,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4859,9 +4651,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4877,9 +4667,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4895,9 +4683,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } /// @@ -4913,8 +4699,6 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 64eee502b..150adb33a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -368,9 +368,7 @@ internal static partial class PorterDuffFunctions where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + return TPixel.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); } <# } #> <# diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 9db3d3004..25fc74e08 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.ColorSpaces.Companding; @@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.PixelFormats; /// /// Flags responsible to select additional operations which could be efficiently applied in -/// +/// /// or -/// +/// /// knowing the pixel type. /// [Flags] @@ -21,7 +21,7 @@ public enum PixelConversionModifiers None = 0, /// - /// Select and instead the standard (non scaled) variants. + /// Select and instead the standard (non scaled) variants. /// Scale = 1 << 0, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 9311c077d..fdf7ccb40 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -87,19 +87,23 @@ public partial struct A8 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A8 FromArgb32(Argb32 source) => new(source.A); + public static A8 FromAbgr32(Abgr32 source) => new(source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A8 FromBgr24(Bgr24 source) => new(byte.MaxValue); + public static A8 FromArgb32(Argb32 source) => new(source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A8 FromBgra32(Bgra32 source) => new(source.A); + public static A8 FromBgr24(Bgr24 source) => new(byte.MaxValue); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A8 FromAbgr32(Abgr32 source) => new(source.A); + public static A8 FromBgra32(Bgra32 source) => new(source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 2a29292a0..65354c807 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -92,7 +92,9 @@ public partial struct Abgr32 : IPixel, IPackedVector /// The alpha component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(float r, float g, float b, float a = 1) - : this() => Pack(r, g, b, a); + : this(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -102,7 +104,9 @@ public partial struct Abgr32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector3 vector) - : this() => Pack(vector); + : this(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -112,7 +116,7 @@ public partial struct Abgr32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector4 vector) - : this() => Pack(vector); + : this() => this = Pack(vector); /// /// Initializes a new instance of the struct. @@ -170,7 +174,7 @@ public partial struct Abgr32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + public readonly Rgba32 ToRgba32() => Rgba32.FromAbgr32(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -204,11 +208,15 @@ public partial struct Abgr32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Abgr32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); + public static Abgr32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Abgr32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + public static Abgr32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 51ef76ad6..50557880a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -85,7 +85,9 @@ public partial struct Argb32 : IPixel, IPackedVector /// The alpha component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(float r, float g, float b, float a = 1) - : this() => Pack(r, g, b, a); + : this(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -95,7 +97,9 @@ public partial struct Argb32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector3 vector) - : this() => Pack(vector); + : this(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -105,7 +109,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector4 vector) - : this() => Pack(vector); + : this() => this = Pack(vector); /// /// Initializes a new instance of the struct. @@ -163,7 +167,7 @@ public partial struct Argb32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + public readonly Rgba32 ToRgba32() => Rgba32.FromArgb32(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -193,19 +197,23 @@ public partial struct Argb32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Argb32 FromArgb32(Argb32 source) => new() { PackedValue = source.PackedValue }; + public static Argb32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Argb32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); + public static Argb32 FromArgb32(Argb32 source) => new() { PackedValue = source.PackedValue }; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Argb32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); + public static Argb32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Argb32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); + public static Argb32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -276,23 +284,6 @@ public partial struct Argb32 : IPixel, IPackedVector /// public override readonly int GetHashCode() => this.Argb.GetHashCode(); - /// - /// Packs the four floats into a color. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Argb32 Pack(float x, float y, float z, float w) => Pack(new Vector4(x, y, z, w)); - - /// - /// Packs a into a uint. - /// - /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Argb32 Pack(Vector3 vector) => Pack(new Vector4(vector, 1f)); - /// /// Packs a into a color. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 4756f5e19..12e6736b7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -15,37 +15,44 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The red component. -/// The green component. -/// The blue component. [StructLayout(LayoutKind.Explicit)] -[method: MethodImpl(MethodImplOptions.AggressiveInlining)] -public partial struct Bgr24(byte r, byte g, byte b) : IPixel +public partial struct Bgr24 : IPixel { /// /// The blue component. /// [FieldOffset(0)] - public byte B = b; + public byte B; /// /// The green component. /// [FieldOffset(1)] - public byte G = g; + public byte G; /// /// The red component. /// [FieldOffset(2)] - public byte R = r; + public byte R; private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Bgr24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + /// /// Compares two objects for equality. /// @@ -70,7 +77,7 @@ public partial struct Bgr24(byte r, byte g, byte b) : IPixel /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B); + public readonly Rgba32 ToRgba32() => Rgba32.FromBgr24(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -106,10 +113,18 @@ public partial struct Bgr24(byte r, byte g, byte b) : IPixel return new(result.GetElement(0), result.GetElement(4), result.GetElement(8)); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgr24 FromArgb32(Argb32 source) => new(source.R, source.G, source.B); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgr24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); @@ -146,10 +161,6 @@ public partial struct Bgr24(byte r, byte g, byte b) : IPixel [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgr24 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Bgr24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgr24 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 4ea149dda..fe26bb8fd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -75,6 +75,9 @@ public partial struct Bgr565(Vector3 vector) : IPixel, IPackedVector public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + /// + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgr565 FromScaledVector4(Vector4 source) => FromVector4(source); @@ -83,6 +86,58 @@ public partial struct Bgr565(Vector3 vector) : IPixel, IPackedVector new() { PackedValue = Pack(new Vector3(source.X, source.Y, source.Z)) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index aeffd5760..fc5d55e64 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -116,7 +116,7 @@ public partial struct Bgra32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + public readonly Rgba32 ToRgba32() => Rgba32.FromBgra32(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -146,11 +146,15 @@ public partial struct Bgra32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Bgra32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + public static Bgra32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Bgra32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); + public static Bgra32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index baaa49ce1..4c89d8e94 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the components for the packed vector. -public partial struct Bgra4444(Vector4 vector) : IPixel, IPackedVector +public partial struct Bgra4444 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -30,8 +26,14 @@ public partial struct Bgra4444(Vector4 vector) : IPixel, IPackedVector { } + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the components for the packed vector. + public Bgra4444(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public ushort PackedValue { get; set; } = Pack(vector); + public ushort PackedValue { get; set; } /// /// Compares two objects for equality. @@ -55,6 +57,10 @@ public partial struct Bgra4444(Vector4 vector) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); @@ -90,6 +96,58 @@ public partial struct Bgra4444(Vector4 vector) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bgra4444 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is Bgra4444 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 381f4628f..fde937fb5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -13,13 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// -/// The vector containing the components for the packed vector. -/// -public partial struct Bgra5551(Vector4 vector) : IPixel, IPackedVector +public partial struct Bgra5551 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -33,8 +27,16 @@ public partial struct Bgra5551(Vector4 vector) : IPixel, IPackedVector { } + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Bgra5551(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public ushort PackedValue { get; set; } = Pack(vector); + public ushort PackedValue { get; set; } /// /// Compares two objects for equality. @@ -58,6 +60,10 @@ public partial struct Bgra5551(Vector4 vector) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); @@ -90,7 +96,55 @@ public partial struct Bgra5551(Vector4 vector) : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Bgra5551 FromBgra5551(Bgra5551 source) => new() { PackedValue = source.PackedValue }; + public static Bgra5551 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); /// public override readonly bool Equals(object? obj) => obj is Bgra5551 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index fba320672..8b4384ff7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -15,19 +15,8 @@ namespace SixLabors.ImageSharp.PixelFormats; /// public partial struct Byte4 : IPixel, IPackedVector { - /// - /// The maximum byte value. - /// private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); - /// - /// Initializes a new instance of the struct. - /// - /// - /// A vector containing the initial values for the components of the Byte4 structure. - /// - public Byte4(Vector4 vector) => this.PackedValue = Pack(vector); - /// /// Initializes a new instance of the struct. /// @@ -36,11 +25,18 @@ public partial struct Byte4 : IPixel, IPackedVector /// The z-component /// The w-component public Byte4(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) { - Vector4 vector = new(x, y, z, w); - this.PackedValue = Pack(vector); } + /// + /// Initializes a new instance of the struct. + /// + /// + /// A vector containing the initial values for the components of the Byte4 structure. + /// + public Byte4(Vector4 vector) => this.PackedValue = Pack(vector); + /// public uint PackedValue { get; set; } @@ -100,10 +96,58 @@ public partial struct Byte4 : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Byte4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Byte4 FromRgba32(Rgba32 source) => new() { PackedValue = source.PackedValue }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is Byte4 byte4 && this.Equals(byte4); @@ -130,12 +174,10 @@ public partial struct Byte4 : IPixel, IPackedVector { vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); - Vector128 result = Vector128.ConvertToUInt32(vector.AsVector128()); - - uint byte4 = result.GetElement(0) & 0xFF; - uint byte3 = result.GetElement(1) << 8; - uint byte2 = result.GetElement(2) << 16; - uint byte1 = result.GetElement(3) << 24; + uint byte4 = (uint)Math.Round(vector.X) & 0xFF; + uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 0x8; + uint byte2 = ((uint)Math.Round(vector.Z) & 0xFF) << 0x10; + uint byte1 = ((uint)Math.Round(vector.W) & 0xFF) << 0x18; return byte4 | byte3 | byte2 | byte1; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 2d8ab5ff0..45cc061ae 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -12,14 +12,16 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The single component value. -public partial struct HalfSingle(float value) : IPixel, IPackedVector +public partial struct HalfSingle : IPixel, IPackedVector { + /// + /// Initializes a new instance of the struct. + /// + /// The single component value. + public HalfSingle(float value) => this.PackedValue = HalfTypeHelper.Pack(value); + /// - public ushort PackedValue { get; set; } = HalfTypeHelper.Pack(value); + public ushort PackedValue { get; set; } /// /// Compares two objects for equality. @@ -43,6 +45,10 @@ public partial struct HalfSingle(float value) : IPixel, IPackedVecto [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -80,6 +86,58 @@ public partial struct HalfSingle(float value) : IPixel, IPackedVecto [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HalfSingle FromVector4(Vector4 source) => new() { PackedValue = HalfTypeHelper.Pack(source.X) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 525638b1d..1fd312523 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -52,6 +52,10 @@ public partial struct HalfVector2 : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -93,6 +97,58 @@ public partial struct HalfVector2 : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HalfVector2 FromVector4(Vector4 source) => new() { PackedValue = Pack(source.X, source.Y) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 28f3849eb..4741bcf9e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// A vector containing the initial values for the components -public partial struct HalfVector4(Vector4 vector) : IPixel, IPackedVector +public partial struct HalfVector4 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -30,8 +26,14 @@ public partial struct HalfVector4(Vector4 vector) : IPixel, IPacked { } + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the initial values for the components + public HalfVector4(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public ulong PackedValue { get; set; } = Pack(vector); + public ulong PackedValue { get; set; } /// /// Compares two objects for equality. @@ -55,13 +57,17 @@ public partial struct HalfVector4(Vector4 vector) : IPixel, IPacked [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { Vector4 scaled = this.ToVector4(); scaled += Vector4.One; - scaled /= 2F; + scaled /= 2f; return scaled; } @@ -87,7 +93,7 @@ public partial struct HalfVector4(Vector4 vector) : IPixel, IPacked [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HalfVector4 FromScaledVector4(Vector4 source) { - source *= 2F; + source *= 2f; source -= Vector4.One; return FromVector4(source); } @@ -96,6 +102,58 @@ public partial struct HalfVector4(Vector4 vector) : IPixel, IPacked [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HalfVector4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is HalfVector4 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 52bb85fe2..41578a6f6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -12,16 +12,18 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The luminance component -public partial struct L16(ushort luminance) : IPixel, IPackedVector +public partial struct L16 : IPixel, IPackedVector { private const float Max = ushort.MaxValue; + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component + public L16(ushort luminance) => this.PackedValue = luminance; + /// - public ushort PackedValue { get; set; } = luminance; + public ushort PackedValue { get; set; } /// /// Compares two objects for equality. @@ -85,19 +87,23 @@ public partial struct L16(ushort luminance) : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L16 FromArgb32(Argb32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); + public static L16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); + public static L16 FromArgb32(Argb32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); + public static L16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); + public static L16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 7c47ab10c..90731f6e9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -13,24 +13,19 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The luminance component. -public partial struct L8(byte luminance) : IPixel, IPackedVector +public partial struct L8 : IPixel, IPackedVector { - /// - /// The maximum byte value. - /// private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); /// - /// The half vector value. + /// Initializes a new instance of the struct. /// - private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); + /// The luminance component. + public L8(byte luminance) => this.PackedValue = luminance; /// - public byte PackedValue { get; set; } = luminance; + public byte PackedValue { get; set; } /// /// Compares two objects for equality. @@ -94,19 +89,23 @@ public partial struct L8(byte luminance) : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L8 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); + public static L8 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L8 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); + public static L8 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L8 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); + public static L8 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static L8 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); + public static L8 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 558f15332..948f0b144 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -14,13 +14,8 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The luminance component. -/// The alpha component. [StructLayout(LayoutKind.Explicit)] -public partial struct La16(byte l, byte a) : IPixel, IPackedVector +public partial struct La16 : IPixel, IPackedVector { /// /// The maximum byte value. @@ -36,13 +31,24 @@ public partial struct La16(byte l, byte a) : IPixel, IPackedVector /// Gets or sets the luminance component. /// [FieldOffset(0)] - public byte L = l; + public byte L; /// /// Gets or sets the alpha component. /// [FieldOffset(1)] - public byte A = a; + public byte A; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component. + /// The alpha component. + public La16(byte l, byte a) + { + this.L = l; + this.A = a; + } /// public ushort PackedValue @@ -110,19 +116,23 @@ public partial struct La16(byte l, byte a) : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La16 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); + public static La16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); + public static La16 FromArgb32(Argb32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); + public static La16 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La16 FromAbgr32(Abgr32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); + public static La16 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index c0d024356..0381973d9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -13,13 +13,8 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The luminance component. -/// The alpha component. [StructLayout(LayoutKind.Explicit)] -public partial struct La32(ushort l, ushort a) : IPixel, IPackedVector +public partial struct La32 : IPixel, IPackedVector { private const float Max = ushort.MaxValue; @@ -27,13 +22,24 @@ public partial struct La32(ushort l, ushort a) : IPixel, IPackedVector [FieldOffset(0)] - public ushort L = l; + public ushort L; /// /// Gets or sets the alpha component. /// [FieldOffset(2)] - public ushort A = a; + public ushort A; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component. + /// The alpha component. + public La32(ushort l, ushort a) + { + this.L = l; + this.A = a; + } /// public uint PackedValue @@ -107,7 +113,7 @@ public partial struct La32(ushort l, ushort a) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La32 FromArgb32(Argb32 source) + public static La32 FromAbgr32(Abgr32 source) { ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); ushort a = ColorNumerics.From8BitTo16Bit(source.A); @@ -116,20 +122,24 @@ public partial struct La32(ushort l, ushort a) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La32 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), ushort.MaxValue); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La32 FromBgra32(Bgra32 source) + public static La32 FromArgb32(Argb32 source) { ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); ushort a = ColorNumerics.From8BitTo16Bit(source.A); return new(l, a); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static La32 FromAbgr32(Abgr32 source) + public static La32 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), ushort.MaxValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgra32(Bgra32 source) { ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); ushort a = ColorNumerics.From8BitTo16Bit(source.A); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index 138490b9c..e901adc9e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct NormalizedByte2(Vector2 vector) : IPixel, IPackedVector +public partial struct NormalizedByte2 : IPixel, IPackedVector { private const float MaxPos = 127f; private static readonly Vector2 Half = new(MaxPos); @@ -32,8 +28,14 @@ public partial struct NormalizedByte2(Vector2 vector) : IPixel, { } + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedByte2(Vector2 vector) => this.PackedValue = Pack(vector); + /// - public ushort PackedValue { get; set; } = Pack(vector); + public ushort PackedValue { get; set; } /// /// Compares two objects for equality. @@ -57,6 +59,10 @@ public partial struct NormalizedByte2(Vector2 vector) : IPixel, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -94,6 +100,58 @@ public partial struct NormalizedByte2(Vector2 vector) : IPixel, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static NormalizedByte2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index d9a3cc1cd..1202801ab 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -13,11 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct NormalizedByte4(Vector4 vector) : IPixel, IPackedVector +public partial struct NormalizedByte4 : IPixel, IPackedVector { private const float MaxPos = 127f; private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); @@ -35,8 +31,14 @@ public partial struct NormalizedByte4(Vector4 vector) : IPixel, { } + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedByte4(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public uint PackedValue { get; set; } = Pack(vector); + public uint PackedValue { get; set; } /// /// Compares two objects for equality. @@ -60,6 +62,10 @@ public partial struct NormalizedByte4(Vector4 vector) : IPixel, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -97,6 +103,58 @@ public partial struct NormalizedByte4(Vector4 vector) : IPixel, return FromVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static NormalizedByte4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 8971e9879..e15cdafa7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct NormalizedShort2(Vector2 vector) : IPixel, IPackedVector +public partial struct NormalizedShort2 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -34,8 +30,14 @@ public partial struct NormalizedShort2(Vector2 vector) : IPixel + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedShort2(Vector2 vector) => this.PackedValue = Pack(vector); + /// - public uint PackedValue { get; set; } = Pack(vector); + public uint PackedValue { get; set; } /// /// Compares two objects for equality. @@ -59,6 +61,10 @@ public partial struct NormalizedShort2(Vector2 vector) : IPixel !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -96,6 +102,58 @@ public partial struct NormalizedShort2(Vector2 vector) : IPixel new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 727198076..f692f73b2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -13,11 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct NormalizedShort4(Vector4 vector) : IPixel, IPackedVector +public partial struct NormalizedShort4 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -36,8 +32,14 @@ public partial struct NormalizedShort4(Vector4 vector) : IPixel + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedShort4(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public ulong PackedValue { get; set; } = Pack(vector); + public ulong PackedValue { get; set; } /// /// Compares two objects for equality. @@ -61,6 +63,10 @@ public partial struct NormalizedShort4(Vector4 vector) : IPixel !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -102,6 +108,58 @@ public partial struct NormalizedShort4(Vector4 vector) : IPixel new() { PackedValue = Pack(source) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is NormalizedShort4 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs index 131191ee2..da0e6ec60 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs index 17ca5edd8..d459fc4d6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs index f2a5eb28b..608d618d4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs index 05b198636..36ffff714 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs index 7217b7c0b..87175fe21 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs index 0d77f8566..d2c2b2db7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs index 5f516f094..1a0be8b6f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs index ea11e5309..3a2856125 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra5551.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs index 0946dd4c7..c012547c9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs index f24ed6fae..a5e4e3048 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs @@ -21,337 +21,316 @@ public partial struct Abgr32 internal partial class PixelOperations : PixelOperations { /// - public override void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToRgba32(sourceBytes, destinationBytes); } /// public override void FromRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToArgb32(sourceBytes, destinationBytes); } /// public override void FromArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToBgra32(sourceBytes, destinationBytes); } /// public override void FromBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToRgb24(sourceBytes, destinationBytes); } /// public override void FromRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToAbgr32(sourceBytes, destinationBytes); } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToBgr24(sourceBytes, destinationBytes); } /// public override void FromBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToAbgr32(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToAbgr32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToAbgr32(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs index 37ac39a85..30fed8d1c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs @@ -21,337 +21,316 @@ public partial struct Argb32 internal partial class PixelOperations : PixelOperations { /// - public override void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToArgb32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToRgba32(sourceBytes, destinationBytes); } /// public override void FromRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToArgb32(sourceBytes, destinationBytes); } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void FromAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToArgb32(sourceBytes, destinationBytes); } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToBgra32(sourceBytes, destinationBytes); } /// public override void FromBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToArgb32(sourceBytes, destinationBytes); } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToRgb24(sourceBytes, destinationBytes); } /// public override void FromRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToArgb32(sourceBytes, destinationBytes); } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToBgr24(sourceBytes, destinationBytes); } /// public override void FromBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToArgb32(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToArgb32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToArgb32(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs index f604d6f97..e3ff3a7fe 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs @@ -21,337 +21,316 @@ public partial struct Bgr24 internal partial class PixelOperations : PixelOperations { /// - public override void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToBgr24(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToRgba32(sourceBytes, destinationBytes); } /// public override void FromRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToBgr24(sourceBytes, destinationBytes); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToArgb32(sourceBytes, destinationBytes); } /// public override void FromArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToBgr24(sourceBytes, destinationBytes); } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToAbgr32(sourceBytes, destinationBytes); } /// public override void FromAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToBgr24(sourceBytes, destinationBytes); } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToBgra32(sourceBytes, destinationBytes); } /// public override void FromBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToBgr24(sourceBytes, destinationBytes); } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToRgb24(sourceBytes, destinationBytes); } /// public override void FromRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToBgr24(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToBgr24(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToBgr24(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs index b9f7a49e1..ae6be4401 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs @@ -21,337 +21,316 @@ public partial struct Bgra32 internal partial class PixelOperations : PixelOperations { /// - public override void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToBgra32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToRgba32(sourceBytes, destinationBytes); } /// public override void FromRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToBgra32(sourceBytes, destinationBytes); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToArgb32(sourceBytes, destinationBytes); } /// public override void FromArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToBgra32(sourceBytes, destinationBytes); } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void FromAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToBgra32(sourceBytes, destinationBytes); } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToRgb24(sourceBytes, destinationBytes); } /// public override void FromRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToBgra32(sourceBytes, destinationBytes); } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToBgr24(sourceBytes, destinationBytes); } /// public override void FromBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToBgra32(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToBgra32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToBgra32(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs index c1ba4f061..9e877cb9d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct Bgra5551 internal partial class PixelOperations : PixelOperations { /// - public override void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToBgra5551(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToBgra5551(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs index c38d752ea..21c782456 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct L16 internal partial class PixelOperations : PixelOperations { /// - public override void FromL16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromL16(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToL16(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromL16(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToL16(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToL16(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs index 656a0546b..6a7c45800 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct L8 internal partial class PixelOperations : PixelOperations { /// - public override void FromL8(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromL8(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToL8(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromL8(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToL8(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToL8(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs index 82be1323c..2346f32e1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct La16 internal partial class PixelOperations : PixelOperations { /// - public override void FromLa16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromLa16(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToLa16(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromLa16(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToLa16(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToLa16(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs index a9ee9d6b7..d949d61e0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct La32 internal partial class PixelOperations : PixelOperations { /// - public override void FromLa32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromLa32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToLa32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromLa32(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToLa32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToLa32(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs index 1d87121e9..fd6465a22 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs @@ -21,337 +21,316 @@ public partial struct Rgb24 internal partial class PixelOperations : PixelOperations { /// - public override void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToRgb24(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToRgba32(sourceBytes, destinationBytes); } /// public override void FromRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToRgb24(sourceBytes, destinationBytes); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToArgb32(sourceBytes, destinationBytes); } /// public override void FromArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToRgb24(sourceBytes, destinationBytes); } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToAbgr32(sourceBytes, destinationBytes); } /// public override void FromAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToRgb24(sourceBytes, destinationBytes); } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToBgra32(sourceBytes, destinationBytes); } /// public override void FromBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToRgb24(sourceBytes, destinationBytes); } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToBgr24(sourceBytes, destinationBytes); } /// public override void FromBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToRgb24(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToRgb24(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToRgb24(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs index 60cfdad4b..69e06da21 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct Rgb48 internal partial class PixelOperations : PixelOperations { /// - public override void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToRgb48(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToRgb48(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToRgb48(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs index da7c9a6c9..8a98521f0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs @@ -21,317 +21,296 @@ public partial struct Rgba32 internal partial class PixelOperations : PixelOperations { /// - public override void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToRgba32(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToArgb32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToArgb32(sourceBytes, destinationBytes); } /// public override void FromArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromArgb32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromArgb32.ToRgba32(sourceBytes, destinationBytes); } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToAbgr32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToAbgr32(sourceBytes, destinationBytes); } /// public override void FromAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromAbgr32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromAbgr32.ToRgba32(sourceBytes, destinationBytes); } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToBgra32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToBgra32(sourceBytes, destinationBytes); } /// public override void FromBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgra32.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgra32.ToRgba32(sourceBytes, destinationBytes); } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToRgb24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToRgb24(sourceBytes, destinationBytes); } /// public override void FromRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgb24.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgb24.ToRgba32(sourceBytes, destinationBytes); } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromRgba32.ToBgr24(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromRgba32.ToBgr24(sourceBytes, destinationBytes); } /// public override void FromBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); - Span dest = MemoryMarshal.Cast(destinationPixels); - PixelConverter.FromBgr24.ToRgba32(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast(source); + Span destinationBytes = MemoryMarshal.Cast(destination); + PixelConverter.FromBgr24.ToRgba32(sourceBytes, destinationBytes); } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba64( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToRgba32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToRgba32(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs index b6236f8a6..4679e950e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs @@ -21,282 +21,246 @@ public partial struct Rgba64 internal partial class PixelOperations : PixelOperations { /// - public override void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + public override void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + public override void ToRgba64(Configuration configuration, ReadOnlySpan source, Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// public override void ToArgb32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Argb32.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToAbgr32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgr24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL8( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = L8.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToL16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = L16.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa16( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref La16 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = La16.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToLa32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref La32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = La32.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb24( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgba32( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToRgb48( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void ToBgra5551( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) { - PixelOperations.Instance.ToRgba64(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.ToRgba64(configuration, source, destination.Slice(0, source.Length)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude index f7e178d7f..c4d5a6b95 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -57,10 +57,10 @@ using SixLabors.ImageSharp.PixelFormats.Utils; /// public override void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span<<#=pixelType#>> destinationPixels) + ReadOnlySpan source, + Span<<#=pixelType#>> destination) { - PixelOperations.Instance.To<#=pixelType#>(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + PixelOperations.Instance.To<#=pixelType#>(configuration, source, destination.Slice(0, source.Length)); } <#+ } @@ -68,21 +68,21 @@ using SixLabors.ImageSharp.PixelFormats.Utils; void GenerateDefaultSelfConversionMethods(string pixelType) { #>/// - public override void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span<<#=pixelType#>> destinationPixels) + public override void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span<<#=pixelType#>> destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - source.CopyTo(destinationPixels.Slice(0, source.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } /// - public override void To<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<<#=pixelType#>> destinationPixels) + public override void To<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span<<#=pixelType#>> destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + source.CopyTo(destination.Slice(0, source.Length)); } <#+ } @@ -94,21 +94,18 @@ using SixLabors.ImageSharp.PixelFormats.Utils; /// public override void To<#=toPixelType#>( Configuration configuration, - ReadOnlySpan<<#=fromPixelType#>> sourcePixels, - Span<<#=toPixelType#>> destinationPixels) + ReadOnlySpan<<#=fromPixelType#>> source, + Span<<#=toPixelType#>> destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref <#=fromPixelType#> sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref <#=toPixelType#> destRef = ref MemoryMarshal.GetReference(destinationPixels); + ref <#=fromPixelType#> sourceBase = ref MemoryMarshal.GetReference(source); + ref <#=toPixelType#> destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref <#=fromPixelType#> sp = ref Unsafe.Add(ref sourceRef, i); - ref <#=toPixelType#> dp = ref Unsafe.Add(ref destRef, i); - - dp.From<#=fromPixelType#>(sp); + Unsafe.Add(ref destinationBase, i) = <#=toPixelType#>.From<#=fromPixelType#>(Unsafe.Add(ref sourceBase, i)); } } <#+ @@ -121,29 +118,29 @@ using SixLabors.ImageSharp.PixelFormats.Utils; /// public override void To<#=otherPixelType#>( Configuration configuration, - ReadOnlySpan<<#=thisPixelType#>> sourcePixels, - Span<<#=otherPixelType#>> destinationPixels) + ReadOnlySpan<<#=thisPixelType#>> source, + Span<<#=otherPixelType#>> destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast<<#=thisPixelType#>, byte>(sourcePixels); - Span dest = MemoryMarshal.Cast<<#=otherPixelType#>, byte>(destinationPixels); - PixelConverter.From<#=thisPixelType#>.To<#=otherPixelType#>(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast<<#=thisPixelType#>, byte>(source); + Span destinationBytes = MemoryMarshal.Cast<<#=otherPixelType#>, byte>(destination); + PixelConverter.From<#=thisPixelType#>.To<#=otherPixelType#>(sourceBytes, destinationBytes); } /// public override void From<#=otherPixelType#>( Configuration configuration, - ReadOnlySpan<<#=otherPixelType#>> sourcePixels, - Span<<#=thisPixelType#>> destinationPixels) + ReadOnlySpan<<#=otherPixelType#>> source, + Span<<#=thisPixelType#>> destination) { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ReadOnlySpan source = MemoryMarshal.Cast<<#=otherPixelType#>, byte>(sourcePixels); - Span dest = MemoryMarshal.Cast<<#=thisPixelType#>, byte>(destinationPixels); - PixelConverter.From<#=otherPixelType#>.To<#=thisPixelType#>(source, dest); + ReadOnlySpan sourceBytes = MemoryMarshal.Cast<<#=otherPixelType#>, byte>(source); + Span destinationBytes = MemoryMarshal.Cast<<#=thisPixelType#>, byte>(destination); + PixelConverter.From<#=otherPixelType#>.To<#=thisPixelType#>(sourceBytes, destinationBytes); } <#+ } @@ -161,20 +158,20 @@ using SixLabors.ImageSharp.PixelFormats.Utils; public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span<<#=pixelType#>> destinationPixels, + Span<<#=pixelType#>> destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(<#=removeTheseModifiers#>)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(<#=removeTheseModifiers#>)); } /// public override void ToVector4( Configuration configuration, - ReadOnlySpan<<#=pixelType#>> sourcePixels, - Span destVectors, + ReadOnlySpan<<#=pixelType#>> source, + Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(<#=removeTheseModifiers#>)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(<#=removeTheseModifiers#>)); } <#+ } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs index 8343b4b3c..770f3a1de 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs index 9a2bdd260..160ab9bd0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs index 0590b43c8..703138454 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs index fc7a81ae2..c9714c217 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs index c97af4e34..e7b463fe0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs index 9be38ac4e..316c96565 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs index 824618c65..34a865533 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La32.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs index 848d0d6f3..36f4a0bf5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs index 79319070d..e67321e4f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs index 0b5f23bc5..99636cb37 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs index 21634a2b3..ad6aa8d8a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs index e0e117727..7bca1c781 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs index b4dd4fcc3..435a8c077 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs index 26f216ae2..c9ed730c4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs index 36b9f85f7..ed89585dc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs index f796098c6..6b362d44e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index 128fd8daf..5f9b51af9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs @@ -2,9 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -55,43 +53,5 @@ public partial struct RgbaVector MemoryMarshal.Cast(sourcePixels).CopyTo(destinationVectors); Vector4Converters.ApplyForwardConversionModifiers(destinationVectors, modifiers); } - - public override void ToL8( - Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) - { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); - - ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); - ref L8 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) - { - ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.Pack(sp); - } - } - - public override void ToL16( - Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) - { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); - - ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); - ref L16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) - { - ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.Pack(sp); - } - } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs index 737cd5299..d8225f18a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs index ab069c0ab..3d7043b0c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; - namespace SixLabors.ImageSharp.PixelFormats; /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index c31bfc613..0dea02512 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct Rg32(Vector2 vector) : IPixel, IPackedVector +public partial struct Rg32 : IPixel, IPackedVector { private static readonly Vector2 Max = new(ushort.MaxValue); @@ -30,8 +26,14 @@ public partial struct Rg32(Vector2 vector) : IPixel, IPackedVector { } + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rg32(Vector2 vector) => this.PackedValue = Pack(vector); + /// - public uint PackedValue { get; set; } = Pack(vector); + public uint PackedValue { get; set; } /// /// Compares two objects for equality. @@ -90,21 +92,25 @@ public partial struct Rg32(Vector2 vector) : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rg32 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromAbgr32(Abgr32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rg32 FromArgb32(Argb32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rg32 FromBgr24(Bgr24 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); + public static Rg32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rg32 FromBgra32(Bgra32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); + public static Rg32 FromBgr24(Bgr24 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rg32 FromAbgr32(Abgr32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); + public static Rg32 FromBgra32(Bgra32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index fc54accfd..a4445bd71 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -15,37 +15,44 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The red component. -/// The green component. -/// The blue component. [StructLayout(LayoutKind.Explicit)] -[method: MethodImpl(MethodImplOptions.AggressiveInlining)] -public partial struct Rgb24(byte r, byte g, byte b) : IPixel +public partial struct Rgb24 : IPixel { /// /// The red component. /// [FieldOffset(0)] - public byte R = r; + public byte R; /// /// The green component. /// [FieldOffset(1)] - public byte G = g; + public byte G; /// /// The blue component. /// [FieldOffset(2)] - public byte B = b; + public byte B; private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + /// /// Allows the implicit conversion of an instance of to a /// . @@ -77,6 +84,10 @@ public partial struct Rgb24(byte r, byte g, byte b) : IPixel [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromRgb24(this); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); @@ -95,10 +106,6 @@ public partial struct Rgb24(byte r, byte g, byte b) : IPixel /// public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgb24 FromScaledVector4(Vector4 source) => FromVector4(source); @@ -117,19 +124,23 @@ public partial struct Rgb24(byte r, byte g, byte b) : IPixel /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb24 FromArgb32(Argb32 source) => new(source.R, source.G, source.B); + public static Rgb24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); + public static Rgb24 FromArgb32(Argb32 source) => new(source.R, source.G, source.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb24 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B); + public static Rgb24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb24 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); + public static Rgb24 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index af901e3d2..d4f1cabb2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -71,8 +71,7 @@ public partial struct Rgb48 : IPixel /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() - => new(ColorNumerics.From16BitTo8Bit(this.R), ColorNumerics.From16BitTo8Bit(this.G), ColorNumerics.From16BitTo8Bit(this.B)); + public readonly Rgba32 ToRgba32() => Rgba32.FromRgb48(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -104,11 +103,20 @@ public partial struct Rgb48 : IPixel return new((ushort)MathF.Round(source.X), (ushort)MathF.Round(source.Y), (ushort)MathF.Round(source.Z)); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromAbgr32(Abgr32 source) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgb48 FromArgb32(Argb32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgb48 FromBgr24(Bgr24 source) @@ -119,10 +127,6 @@ public partial struct Rgb48 : IPixel public static Rgb48 FromBgra32(Bgra32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb48 FromRgba64(Rgba64 source) => new(source.R, source.G, source.B); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgb48 FromL8(L8 source) @@ -157,14 +161,13 @@ public partial struct Rgb48 : IPixel public static Rgb48 FromRgba32(Rgba32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb48 FromAbgr32(Abgr32 source) - => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(source.B)); + public static Rgb48 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb48 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B); + public static Rgb48 FromRgba64(Rgba64 source) => new(source.R, source.G, source.B); /// public override readonly bool Equals(object? obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 85ae045a0..0f7be1774 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -13,11 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct Rgba1010102(Vector4 vector) : IPixel, IPackedVector +public partial struct Rgba1010102 : IPixel, IPackedVector { private static readonly Vector4 Multiplier = new(1023F, 1023F, 1023F, 3F); @@ -33,8 +29,14 @@ public partial struct Rgba1010102(Vector4 vector) : IPixel, IPacked { } + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rgba1010102(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public uint PackedValue { get; set; } = Pack(vector); + public uint PackedValue { get; set; } /// /// Compares two objects for equality. @@ -58,6 +60,10 @@ public partial struct Rgba1010102(Vector4 vector) : IPixel, IPacked [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); @@ -88,6 +94,58 @@ public partial struct Rgba1010102(Vector4 vector) : IPixel, IPacked [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgba1010102 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is Rgba1010102 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 83c376c9f..8b5779a53 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -87,7 +87,9 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The alpha component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(float r, float g, float b, float a = 1) - : this() => Pack(r, g, b, a); + : this(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -97,7 +99,9 @@ public partial struct Rgba32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(Vector3 vector) - : this() => Pack(vector); + : this(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -296,19 +300,23 @@ public partial struct Rgba32 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + public static Rgba32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); + public static Rgba32 FromArgb32(Argb32 source) => new(source.R, source.G, source.B, source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); + public static Rgba32 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); + public static Rgba32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, source.A); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -386,23 +394,6 @@ public partial struct Rgba32 : IPixel, IPackedVector /// public override readonly int GetHashCode() => this.Rgba.GetHashCode(); - /// - /// Packs the four floats into a color. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Rgba32 Pack(float x, float y, float z, float w) => Pack(new Vector4(x, y, z, w)); - - /// - /// Packs a into a uint. - /// - /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Rgba32 Pack(Vector3 vector) => Pack(new Vector4(vector, 1f)); - /// /// Packs a into a color. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 5294064eb..73629461b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -192,12 +192,7 @@ public partial struct Rgba64 : IPixel, IPackedVector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgba32 ToRgba32() - => new( - ColorNumerics.From16BitTo8Bit(this.R), - ColorNumerics.From16BitTo8Bit(this.G), - ColorNumerics.From16BitTo8Bit(this.B), - ColorNumerics.From16BitTo8Bit(this.A)); + public readonly Rgba32 ToRgba32() => Rgba32.FromRgba64(this); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -225,21 +220,25 @@ public partial struct Rgba64 : IPixel, IPackedVector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgba64 FromVector4(Vector4 source) => new(source); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromAbgr32(Abgr32 source) => new(source); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgba64 FromArgb32(Argb32 source) => new(source); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba64 FromBgr24(Bgr24 source) => new(source); + public static Rgba64 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba64 FromBgra32(Bgra32 source) => new(source); + public static Rgba64 FromBgr24(Bgr24 source) => new(source); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgba64 FromAbgr32(Abgr32 source) => new(source); + public static Rgba64 FromBgra32(Bgra32 source) => new(source); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -286,68 +285,35 @@ public partial struct Rgba64 : IPixel, IPackedVector /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Bgra32 ToBgra32() - { - byte r = ColorNumerics.From16BitTo8Bit(this.R); - byte g = ColorNumerics.From16BitTo8Bit(this.G); - byte b = ColorNumerics.From16BitTo8Bit(this.B); - byte a = ColorNumerics.From16BitTo8Bit(this.A); - return new Bgra32(r, g, b, a); - } + public readonly Bgra32 ToBgra32() => Bgra32.FromRgba64(this); /// /// Convert to . /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Argb32 ToArgb32() - { - byte r = ColorNumerics.From16BitTo8Bit(this.R); - byte g = ColorNumerics.From16BitTo8Bit(this.G); - byte b = ColorNumerics.From16BitTo8Bit(this.B); - byte a = ColorNumerics.From16BitTo8Bit(this.A); - return new Argb32(r, g, b, a); - } + public readonly Argb32 ToArgb32() => Argb32.FromRgba64(this); /// /// Convert to . /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Abgr32 ToAbgr32() - { - byte r = ColorNumerics.From16BitTo8Bit(this.R); - byte g = ColorNumerics.From16BitTo8Bit(this.G); - byte b = ColorNumerics.From16BitTo8Bit(this.B); - byte a = ColorNumerics.From16BitTo8Bit(this.A); - return new Abgr32(r, g, b, a); - } + public readonly Abgr32 ToAbgr32() => Abgr32.FromRgba64(this); /// /// Convert to . /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Rgb24 ToRgb24() - { - byte r = ColorNumerics.From16BitTo8Bit(this.R); - byte g = ColorNumerics.From16BitTo8Bit(this.G); - byte b = ColorNumerics.From16BitTo8Bit(this.B); - return new Rgb24(r, g, b); - } + public readonly Rgb24 ToRgb24() => Rgb24.FromRgba64(this); /// /// Convert to . /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Bgr24 ToBgr24() - { - byte r = ColorNumerics.From16BitTo8Bit(this.R); - byte g = ColorNumerics.From16BitTo8Bit(this.G); - byte b = ColorNumerics.From16BitTo8Bit(this.B); - return new Bgr24(r, g, b); - } + public readonly Bgr24 ToBgr24() => Bgr24.FromRgba64(this); /// public override readonly bool Equals(object? obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 0511f3279..60555ad95 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -19,41 +19,49 @@ namespace SixLabors.ImageSharp.PixelFormats; /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// -/// -/// Initializes a new instance of the struct. -/// -/// The red component. -/// The green component. -/// The blue component. -/// The alpha component. [StructLayout(LayoutKind.Sequential)] -[method: MethodImpl(MethodImplOptions.AggressiveInlining)] -public partial struct RgbaVector(float r, float g, float b, float a = 1) : IPixel +public partial struct RgbaVector : IPixel { /// /// Gets or sets the red component. /// - public float R = r; + public float R; /// /// Gets or sets the green component. /// - public float G = g; + public float G; /// /// Gets or sets the blue component. /// - public float B = b; + public float B; /// /// Gets or sets the alpha component. /// - public float A = a; + public float A; private const float MaxBytes = byte.MaxValue; private static readonly Vector4 Max = new(MaxBytes); private static readonly Vector4 Half = new(0.5F); + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaVector(float r, float g, float b, float a = 1) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + /// /// Compares two objects for equality. /// @@ -76,6 +84,10 @@ public partial struct RgbaVector(float r, float g, float b, float a = 1) : IPixe [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(RgbaVector left, RgbaVector right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); @@ -106,6 +118,58 @@ public partial struct RgbaVector(float r, float g, float b, float a = 1) : IPixe return new(source.X, source.Y, source.Z, source.W); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Creates a new instance of the struct. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index e2b652d1a..043599c36 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-32767, -32767, 0, 1] to [32767, 32767, 0, 1] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// The vector containing the component values. -public partial struct Short2(Vector2 vector) : IPixel, IPackedVector +public partial struct Short2 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -37,8 +33,14 @@ public partial struct Short2(Vector2 vector) : IPixel, IPackedVector + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Short2(Vector2 vector) => this.PackedValue = Pack(vector); + /// - public uint PackedValue { get; set; } = Pack(vector); + public uint PackedValue { get; set; } /// /// Compares two objects for equality. @@ -62,6 +64,10 @@ public partial struct Short2(Vector2 vector) : IPixel, IPackedVector !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -99,6 +105,58 @@ public partial struct Short2(Vector2 vector) : IPixel, IPackedVector new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 18d7f22ec..df43703cb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -12,11 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats; /// Ranges from [-37267, -37267, -37267, -37267] to [37267, 37267, 37267, 37267] in vector form. /// /// -/// -/// Initializes a new instance of the struct. -/// -/// A vector containing the initial values for the components. -public partial struct Short4(Vector4 vector) : IPixel, IPackedVector +public partial struct Short4 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; @@ -39,8 +35,14 @@ public partial struct Short4(Vector4 vector) : IPixel, IPackedVector + /// Initializes a new instance of the struct. + /// + /// A vector containing the initial values for the components. + public Short4(Vector4 vector) => this.PackedValue = Pack(vector); + /// - public ulong PackedValue { get; set; } = Pack(vector); + public ulong PackedValue { get; set; } /// /// Compares two objects for equality. @@ -64,6 +66,10 @@ public partial struct Short4(Vector4 vector) : IPixel, IPackedVector !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() @@ -106,6 +112,58 @@ public partial struct Short4(Vector4 vector) : IPixel, IPackedVector new(source); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + /// public override readonly bool Equals(object? obj) => obj is Short4 other && this.Equals(other); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index 7e84bd639..712d43f76 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -14,20 +14,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Argb32 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Argb32 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Argb32 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromArgb32(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromArgb32(Unsafe.Add(ref sourceBase, i)); } } @@ -37,48 +34,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromArgb32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromArgb32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromArgb32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToArgb32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Argb32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Argb32.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToArgb32Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToArgb32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToArgb32(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -86,20 +80,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Abgr32 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Abgr32 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Abgr32 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromAbgr32(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromAbgr32(Unsafe.Add(ref sourceBase, i)); } } @@ -109,48 +100,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromAbgr32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromAbgr32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromAbgr32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Abgr32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Abgr32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Abgr32 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Abgr32.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToAbgr32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToAbgr32Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToAbgr32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToAbgr32(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -158,20 +146,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgr24 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgr24 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgr24 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromBgr24(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromBgr24(Unsafe.Add(ref sourceBase, i)); } } @@ -181,48 +166,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromBgr24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromBgr24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromBgr24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToBgr24(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgr24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Bgr24 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Bgr24.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToBgr24Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToBgr24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToBgr24(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -230,20 +212,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra32 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra32 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra32 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromBgra32(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromBgra32(Unsafe.Add(ref sourceBase, i)); } } @@ -253,48 +232,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromBgra32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromBgra32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromBgra32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToBgra32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Bgra32 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Bgra32.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToBgra32Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToBgra32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToBgra32(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -302,20 +278,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromL8(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromL8(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L8 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L8 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref L8 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromL8(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromL8(Unsafe.Add(ref sourceBase, i)); } } @@ -325,48 +298,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromL8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromL8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromL8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToL8(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L8 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref L8 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = L8.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToL8Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToL8Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToL8(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToL8(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -374,20 +344,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromL16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromL16(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref L16 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref L16 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref L16 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromL16(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromL16(Unsafe.Add(ref sourceBase, i)); } } @@ -397,48 +364,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromL16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromL16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromL16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToL16(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref L16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref L16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = L16.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToL16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToL16Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToL16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToL16(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -446,20 +410,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromLa16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromLa16(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La16 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La16 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref La16 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromLa16(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromLa16(Unsafe.Add(ref sourceBase, i)); } } @@ -469,48 +430,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromLa16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromLa16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromLa16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToLa16(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref La16 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref La16 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = La16.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToLa16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToLa16Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToLa16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToLa16(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -518,20 +476,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromLa32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromLa32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref La32 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref La32 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref La32 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromLa32(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromLa32(Unsafe.Add(ref sourceBase, i)); } } @@ -541,48 +496,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromLa32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromLa32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromLa32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToLa32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref La32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref La32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref La32 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = La32.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToLa32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToLa32Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToLa32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToLa32(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -590,20 +542,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb24 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb24 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb24 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromRgb24(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromRgb24(Unsafe.Add(ref sourceBase, i)); } } @@ -613,48 +562,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromRgb24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromRgb24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromRgb24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToRgb24(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb24 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgb24 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Rgb24.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToRgb24Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToRgb24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToRgb24(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -662,20 +608,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba32 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba32 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba32 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromRgba32(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromRgba32(Unsafe.Add(ref sourceBase, i)); } } @@ -685,48 +628,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromRgba32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromRgba32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromRgba32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToRgba32(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba32 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Rgba32.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToRgba32Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToRgba32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToRgba32(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -734,20 +674,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgb48 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgb48 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgb48 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromRgb48(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromRgb48(Unsafe.Add(ref sourceBase, i)); } } @@ -757,48 +694,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromRgb48Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromRgb48(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromRgb48(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToRgb48(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgb48 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Rgb48.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToRgb48Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToRgb48(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToRgb48(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -806,20 +740,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Rgba64 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Rgba64 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromRgba64(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromRgba64(Unsafe.Add(ref sourceBase, i)); } } @@ -829,48 +760,45 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromRgba64Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromRgba64(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromRgba64(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToRgba64(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Rgba64.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToRgba64Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToRgba64(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToRgba64(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } /// @@ -878,20 +806,17 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + /// The to the destination pixels. + public virtual void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref Bgra5551 sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref Bgra5551 sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref Bgra5551 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromBgra5551(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.FromBgra5551(Unsafe.Add(ref sourceBase, i)); } } @@ -901,47 +826,44 @@ public partial class PixelOperations /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void FromBgra5551Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.FromBgra5551(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + this.FromBgra5551(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destination); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void ToBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra5551 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref Bgra5551 destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Bgra5551 dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = Bgra5551.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra5551Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void ToBgra5551Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.ToBgra5551(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToBgra5551(configuration, source.Slice(0, count), MemoryMarshal.Cast(destination)); } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt index 8ba3398e3..b2697cb4e 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -20,20 +20,17 @@ /// /// A to configure internal operations. /// The source of data. - /// The to the destination pixels. - public virtual void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span destinationPixels) + /// The to the destination pixels. + public virtual void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span destination) { - Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref <#=pixelType#> sourceBaseRef = ref MemoryMarshal.GetReference(source); - ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref <#=pixelType#> sourceBase = ref MemoryMarshal.GetReference(source); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); for (nuint i = 0; i < (uint)source.Length; i++) { - ref <#=pixelType#> sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.From<#=pixelType#>(sp); + Unsafe.Add(ref destinationBase, i) = TPixel.From<#=pixelType#>(Unsafe.Add(ref sourceBase, i)); } } @@ -43,12 +40,12 @@ /// /// A to configure internal operations. /// The to the source bytes. - /// The to the destination pixels. + /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void From<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + public void From<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destination, int count) { - this.From<#=pixelType#>(configuration, MemoryMarshal.Cast>(sourceBytes).Slice(0, count), destinationPixels); + this.From<#=pixelType#>(configuration, MemoryMarshal.Cast>(sourceBytes).Slice(0, count), destination); } <# @@ -58,39 +55,36 @@ { #> /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'source` span to a span of -s. /// /// A to configure internal operations - /// The span of source pixels - /// The destination span of data. - public virtual void To<#=pixelType#>(Configuration configuration, ReadOnlySpan sourcePixels, Span<<#=pixelType#>> destinationPixels) + /// The span of source pixels + /// The destination span of data. + public virtual void To<#=pixelType#>(Configuration configuration, ReadOnlySpan source, Span<<#=pixelType#>> destination) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref <#=pixelType#> destinationBase = ref MemoryMarshal.GetReference(destination); - for (nuint i = 0; i < (uint)sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref <#=pixelType#> dp = ref Unsafe.Add(ref destBaseRef, i); - - dp.FromScaledVector4(sp.ToScaledVector4()); + Unsafe.Add(ref destinationBase, i) = <#=pixelType#>.FromScaledVector4(Unsafe.Add(ref sourceBase, i).ToScaledVector4()); } } /// /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// The layout of the data in 'destination' must be compatible with layout. /// /// A to configure internal operations - /// The to the source pixels. - /// The to the destination bytes. + /// The to the source pixels. + /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void To<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + public void To<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan source, Span destination, int count) { - this.To<#=pixelType#>(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast>(destBytes)); + this.To<#=pixelType#>(configuration, source.Slice(0, count), MemoryMarshal.Cast>(destination)); } <# } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 4d3f9e22d..174b3fae1 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -33,7 +33,7 @@ public partial class PixelOperations public PixelTypeInfo GetPixelTypeInfo() => TPixel.GetPixelTypeInfo(); /// - /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. /// The method is DESTRUCTIVE altering the contents of . /// /// @@ -42,21 +42,21 @@ public partial class PixelOperations /// /// A to configure internal operations /// The to the source vectors. - /// The to the destination colors. + /// The to the destination colors. /// The to apply during the conversion public virtual void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); - Utils.Vector4Converters.Default.FromVector4(sourceVectors, destinationPixels, modifiers); + Utils.Vector4Converters.Default.FromVector4(sourceVectors, destination, modifiers); } /// - /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. /// The method is DESTRUCTIVE altering the contents of . /// /// @@ -65,77 +65,77 @@ public partial class PixelOperations /// /// A to configure internal operations /// The to the source vectors. - /// The to the destination colors. + /// The to the destination colors. public void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels) - => this.FromVector4Destructive(configuration, sourceVectors, destinationPixels, PixelConversionModifiers.None); + Span destination) + => this.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.None); /// /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// /// A to configure internal operations - /// The to the source colors. + /// The to the source colors. /// The to the destination vectors. /// The to apply during the conversion public virtual void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, + ReadOnlySpan source, Span destinationVectors, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); - Utils.Vector4Converters.Default.ToVector4(sourcePixels, destinationVectors, modifiers); + Utils.Vector4Converters.Default.ToVector4(source, destinationVectors, modifiers); } /// /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// /// A to configure internal operations - /// The to the source colors. + /// The to the source colors. /// The to the destination vectors. public void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, + ReadOnlySpan source, Span destinationVectors) - => this.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.None); + => this.ToVector4(configuration, source, destinationVectors, PixelConversionModifiers.None); /// - /// Bulk operation that copies the to in + /// Bulk operation that copies the to in /// format. /// /// The destination pixel type. /// A to configure internal operations. - /// The to the source pixels. - /// The to the destination pixels. + /// The to the source pixels. + /// The to the destination pixels. public virtual void From( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) where TSourcePixel : unmanaged, IPixel { const int sliceLength = 1024; - int numberOfSlices = sourcePixels.Length / sliceLength; + int numberOfSlices = source.Length / sliceLength; using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength); Span vectorSpan = tempVectors.GetSpan(); for (int i = 0; i < numberOfSlices; i++) { int start = i * sliceLength; - ReadOnlySpan s = sourcePixels.Slice(start, sliceLength); - Span d = destinationPixels.Slice(start, sliceLength); + ReadOnlySpan s = source.Slice(start, sliceLength); + Span d = destination.Slice(start, sliceLength); PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale); this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale); } int endOfCompleteSlices = numberOfSlices * sliceLength; - int remainder = sourcePixels.Length - endOfCompleteSlices; + int remainder = source.Length - endOfCompleteSlices; if (remainder > 0) { - ReadOnlySpan s = sourcePixels[endOfCompleteSlices..]; - Span d = destinationPixels[endOfCompleteSlices..]; + ReadOnlySpan s = source[endOfCompleteSlices..]; + Span d = destination[endOfCompleteSlices..]; vectorSpan = vectorSpan[..remainder]; PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale); this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale); @@ -143,23 +143,23 @@ public partial class PixelOperations } /// - /// Bulk operation that copies the to in + /// Bulk operation that copies the to in /// format. /// /// The destination pixel type. /// A to configure internal operations. - /// The to the source pixels. - /// The to the destination pixels. + /// The to the source pixels. + /// The to the destination pixels. public virtual void To( Configuration configuration, - ReadOnlySpan sourcePixels, - Span destinationPixels) + ReadOnlySpan source, + Span destination) where TDestinationPixel : unmanaged, IPixel { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - PixelOperations.Instance.From(configuration, sourcePixels, destinationPixels); + PixelOperations.Instance.From(configuration, source, destination); } /// @@ -190,7 +190,7 @@ public partial class PixelOperations rgb24.R = Unsafe.Add(ref r, i); rgb24.G = Unsafe.Add(ref g, i); rgb24.B = Unsafe.Add(ref b, i); - Unsafe.Add(ref d, i).FromRgb24(rgb24); + Unsafe.Add(ref d, i) = TPixel.FromRgb24(rgb24); } } @@ -212,15 +212,13 @@ public partial class PixelOperations int count = source.Length; - Rgba32 rgba32 = default; - ref float r = ref MemoryMarshal.GetReference(redChannel); ref float g = ref MemoryMarshal.GetReference(greenChannel); ref float b = ref MemoryMarshal.GetReference(blueChannel); ref TPixel src = ref MemoryMarshal.GetReference(source); for (nuint i = 0; i < (uint)count; i++) { - Unsafe.Add(ref src, i).ToRgba32(ref rgba32); + Rgba32 rgba32 = Unsafe.Add(ref src, i).ToRgba32(); Unsafe.Add(ref r, i) = rgba32.R; Unsafe.Add(ref g, i) = rgba32.G; Unsafe.Add(ref b, i) = rgba32.B; diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 4d07a8a9b..d7cac1530 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -21,139 +21,139 @@ internal static partial class Vector4Converters { [MethodImpl(InliningOptions.ShortMethod)] public static void FromVector4( - Span sourceVectors, - Span destPixels, + Span source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { - Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - UnsafeFromVector4(sourceVectors, destPixels, modifiers); + UnsafeFromVector4(source, destination, modifiers); } [MethodImpl(InliningOptions.ShortMethod)] public static void ToVector4( - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - UnsafeToVector4(sourcePixels, destVectors, modifiers); + UnsafeToVector4(source, destination, modifiers); } [MethodImpl(InliningOptions.ShortMethod)] public static void UnsafeFromVector4( - Span sourceVectors, - Span destPixels, + Span source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { - ApplyBackwardConversionModifiers(sourceVectors, modifiers); + ApplyBackwardConversionModifiers(source, modifiers); if (modifiers.IsDefined(PixelConversionModifiers.Scale)) { - UnsafeFromScaledVector4Core(sourceVectors, destPixels); + UnsafeFromScaledVector4Core(source, destination); } else { - UnsafeFromVector4Core(sourceVectors, destPixels); + UnsafeFromVector4Core(source, destination); } } [MethodImpl(InliningOptions.ShortMethod)] public static void UnsafeToVector4( - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { if (modifiers.IsDefined(PixelConversionModifiers.Scale)) { - UnsafeToScaledVector4Core(sourcePixels, destVectors); + UnsafeToScaledVector4Core(source, destination); } else { - UnsafeToVector4Core(sourcePixels, destVectors); + UnsafeToVector4Core(source, destination); } - ApplyForwardConversionModifiers(destVectors, modifiers); + ApplyForwardConversionModifiers(destination, modifiers); } [MethodImpl(InliningOptions.ShortMethod)] private static void UnsafeFromVector4Core( - ReadOnlySpan sourceVectors, - Span destPixels) + ReadOnlySpan source, + Span destination) where TPixel : unmanaged, IPixel { - ref Vector4 sourceStart = ref MemoryMarshal.GetReference(sourceVectors); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceVectors.Length); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); + ref Vector4 sourceStart = ref MemoryMarshal.GetReference(source); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)source.Length); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) { - destRef.FromVector4(sourceStart); + destinationBase = TPixel.FromVector4(sourceStart); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } [MethodImpl(InliningOptions.ShortMethod)] private static void UnsafeToVector4Core( - ReadOnlySpan sourcePixels, - Span destVectors) + ReadOnlySpan source, + Span destination) where TPixel : unmanaged, IPixel { - ref TPixel sourceStart = ref MemoryMarshal.GetReference(sourcePixels); - ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourcePixels.Length); - ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors); + ref TPixel sourceStart = ref MemoryMarshal.GetReference(source); + ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)source.Length); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) { - destRef = sourceStart.ToVector4(); + destinationBase = sourceStart.ToVector4(); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } [MethodImpl(InliningOptions.ShortMethod)] private static void UnsafeFromScaledVector4Core( - ReadOnlySpan sourceVectors, - Span destinationColors) + ReadOnlySpan source, + Span destination) where TPixel : unmanaged, IPixel { - ref Vector4 sourceStart = ref MemoryMarshal.GetReference(sourceVectors); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceVectors.Length); - ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors); + ref Vector4 sourceStart = ref MemoryMarshal.GetReference(source); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)source.Length); + ref TPixel destinationBase = ref MemoryMarshal.GetReference(destination); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) { - destRef.FromScaledVector4(sourceStart); + destinationBase = TPixel.FromScaledVector4(sourceStart); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } [MethodImpl(InliningOptions.ShortMethod)] private static void UnsafeToScaledVector4Core( - ReadOnlySpan sourceColors, - Span destinationVectors) + ReadOnlySpan source, + Span destination) where TPixel : unmanaged, IPixel { - ref TPixel sourceStart = ref MemoryMarshal.GetReference(sourceColors); - ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceColors.Length); - ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors); + ref TPixel sourceStart = ref MemoryMarshal.GetReference(source); + ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)source.Length); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) { - destRef = sourceStart.ToScaledVector4(); + destinationBase = sourceStart.ToScaledVector4(); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index cbf893915..ae891f350 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -125,10 +125,7 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor // Grab the max components of the two pixels ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x); ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x); - - var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4()); - - currentTargetPixel.FromVector4(pixelValue); + currentTargetPixel = TPixel.FromVector4(Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4())); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index f1d7fbd6f..3c0ebb707 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -203,7 +203,7 @@ public readonly partial struct ErrorDither : IDither, IEquatable, I Vector4 result = pixel.ToVector4(); result += error * coefficient; - pixel.FromVector4(result); + pixel = TPixel.FromVector4(result); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index 1a02e7a22..d1f46c744 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -181,8 +181,7 @@ public readonly partial struct OrderedDither : IDither, IEquatable { - Unsafe.SkipInit(out Rgba32 rgba); - source.ToRgba32(ref rgba); + Rgba32 rgba = source.ToRgba32(); Unsafe.SkipInit(out Rgba32 attempt); float factor = spread * this.thresholdMatrix[y % this.modulusY, x % this.modulusX] * scale; @@ -192,10 +191,7 @@ public readonly partial struct OrderedDither : IDither, IEquatable diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 0baa87a61..78085eaab 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -64,11 +64,11 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. - using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) + using (CdfTileData cdfData = new(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) { cdfData.CalculateLookupTables(source, this); - var tileYStartPositions = new List<(int Y, int CdfY)>(); + List<(int Y, int CdfY)> tileYStartPositions = []; int cdfY = 0; int yStart = halfTileHeight; for (int tile = 0; tile < tileCount - 1; tile++) @@ -78,7 +78,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz yStart += tileHeight; } - var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source.PixelBuffer); + RowIntervalOperation operation = new(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source.PixelBuffer); ParallelRowIterator.IterateRowIntervals( this.Configuration, new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), @@ -145,7 +145,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz { ref TPixel pixel = ref rowSpan[dx]; float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + pixel = TPixel.FromVector4(new(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); } } } @@ -191,7 +191,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz { ref TPixel pixel = ref rowSpan[dx]; float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + pixel = TPixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); } tileY++; @@ -243,7 +243,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz { ref TPixel pixel = ref rowSpan[dx]; float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + pixel = TPixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); tileX++; } } @@ -404,10 +404,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz { for (int index = rows.Min; index < rows.Max; index++) { - (int Y, int CdfY) tileYStartPosition = this.tileYStartPositions[index]; - int y = tileYStartPosition.Y; - int cdfYY = tileYStartPosition.CdfY; - + (int y, int cdfY) = this.tileYStartPositions[index]; int cdfX = 0; int x = this.halfTileWidth; for (int tile = 0; tile < this.tileCount - 1; tile++) @@ -430,12 +427,12 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz tileX, tileY, cdfX, - cdfYY, + cdfY, this.tileWidth, this.tileHeight, this.luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + pixel = TPixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); tileX++; } @@ -494,7 +491,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz this.pixelsInTile = tileWidth * tileHeight; // Calculate the start positions and rent buffers. - this.tileYStartPositions = new List<(int Y, int CdfY)>(); + this.tileYStartPositions = []; int cdfY = 0; for (int y = 0; y < sourceHeight; y += tileHeight) { @@ -505,7 +502,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) { - var operation = new RowIntervalOperation( + RowIntervalOperation operation = new( processor, this.memoryAllocator, this.cdfMinBuffer2D, diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index d2bec0b49..93144653e 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -383,7 +383,7 @@ internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : His // Map the current pixel to the new equalized value. int luminance = GetLuminance(this.source[x, y], this.processor.LuminanceLevels); float luminanceEqualized = Unsafe.Add(ref cdfBase, (uint)luminance) / numberOfPixelsMinusCdfMin; - this.targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, this.source[x, y].ToVector4().W)); + this.targetPixels[x, y] = TPixel.FromVector4(new(luminanceEqualized, luminanceEqualized, luminanceEqualized, this.source[x, y].ToVector4().W)); // Remove top most row from the histogram, mirroring rows which exceeds the borders. if (this.useFastPath) diff --git a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs index 72148374a..4fd37d479 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs @@ -55,8 +55,7 @@ internal sealed class EuclideanPixelMap : IDisposable PixelOperations.Instance.ToRgba32(configuration, this.Palette.Span, this.rgbaPalette); this.transparentIndex = transparentIndex; - Unsafe.SkipInit(out this.transparentMatch); - this.transparentMatch.FromRgba32(default); + this.transparentMatch = TPixel.FromRgba32(default); } /// @@ -76,8 +75,7 @@ internal sealed class EuclideanPixelMap : IDisposable public int GetClosestColor(TPixel color, out TPixel match) { ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.Palette.Span); - Unsafe.SkipInit(out Rgba32 rgba); - color.ToRgba32(ref rgba); + Rgba32 rgba = color.ToRgba32(); // Check if the color is in the lookup table if (!this.cache.TryGetValue(rgba, out short index)) diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs index 039417578..8b39b7457 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs @@ -277,8 +277,7 @@ public struct OctreeQuantizer : IQuantizer [MethodImpl(InliningOptions.ShortMethod)] public int GetPaletteIndex(TPixel color) { - Unsafe.SkipInit(out Rgba32 rgba); - color.ToRgba32(ref rgba); + Rgba32 rgba = color.ToRgba32(); return this.root.GetPaletteIndex(ref rgba, 0); } @@ -476,9 +475,7 @@ public struct OctreeQuantizer : IQuantizer Vector3.Zero, new Vector3(255)); - Unsafe.SkipInit(out TPixel pixel); - pixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, byte.MaxValue)); - palette[index] = pixel; + palette[index] = TPixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z)); // Consume the next palette index this.paletteIndex = index++; diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index e13975b56..ba2ab825a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -142,8 +142,7 @@ internal struct WuQuantizer : IQuantizer if (moment.Weight > 0) { - ref TPixel color = ref paletteSpan[k]; - color.FromScaledVector4(moment.Normalize()); + paletteSpan[k] = TPixel.FromScaledVector4(moment.Normalize()); } } @@ -178,8 +177,7 @@ internal struct WuQuantizer : IQuantizer return (byte)this.pixelMap!.GetClosestColor(color, out match); } - Rgba32 rgba = default; - color.ToRgba32(ref rgba); + Rgba32 rgba = color.ToRgba32(); const int shift = 8 - IndexBits; int r = rgba.R >> shift; @@ -549,7 +547,7 @@ internal struct WuQuantizer : IQuantizer /// The first set. /// The second set. /// Returns a value indicating whether the box has been split. - private bool Cut(ref Box set1, ref Box set2) + private readonly bool Cut(ref Box set1, ref Box set2) { ReadOnlySpan momentSpan = this.momentsOwner.GetSpan(); Moment whole = Volume(ref set1, momentSpan); diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs index bd938c9da..6cae20853 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs @@ -49,23 +49,17 @@ public abstract class FromRgba32Bytes for (int i = 0; i < this.Count; i++) { int i4 = i * 4; - var c = default(TPixel); - c.FromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); - d[i] = c; + d[i] = TPixel.FromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); } } [Benchmark(Baseline = true)] public void CommonBulk() - { - new PixelOperations().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } + => new PixelOperations().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); [Benchmark] public void OptimizedBulk() - { - PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } + => PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); } public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index d70c37ccb..ecd16b957 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -49,21 +49,17 @@ public abstract class FromVector4 ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); for (nuint i = 0; i < (uint)this.Count; i++) { - Unsafe.Add(ref d, i).FromVector4(Unsafe.Add(ref s, i)); + Unsafe.Add(ref d, i) = TPixel.FromVector4(Unsafe.Add(ref s, i)); } } [Benchmark(Baseline = true)] public void PixelOperations_Base() - { - new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); - } + => new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); [Benchmark] public void PixelOperations_Specialized() - { - PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); - } + => PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); } public class FromVector4Rgba32 : FromVector4 diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs index 6d3f8f952..19ab780c0 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs @@ -47,8 +47,7 @@ public abstract class ToRgba32Bytes { TPixel c = s[i]; int i4 = i * 4; - Rgba32 rgba = default; - c.ToRgba32(ref rgba); + Rgba32 rgba = c.ToRgba32(); d[i4] = rgba.R; d[i4 + 1] = rgba.G; d[i4 + 2] = rgba.B; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs index 0ee507832..a8c6a021a 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs @@ -12,8 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; public class PixelConversion_Rgba32_To_Argb32 { private Rgba32[] source; - - private Argb32[] dest; + private Argb32[] destination; [Params(64)] public int Count { get; set; } @@ -22,19 +21,19 @@ public class PixelConversion_Rgba32_To_Argb32 public void Setup() { this.source = new Rgba32[this.Count]; - this.dest = new Argb32[this.Count]; + this.destination = new Argb32[this.Count]; } [Benchmark(Baseline = true)] public void Default() { ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; + ref Argb32 dBase = ref this.destination[0]; for (nuint i = 0; i < (uint)this.Count; i++) { Rgba32 s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); + Unsafe.Add(ref dBase, i) = Argb32.FromRgba32(s); } } @@ -48,21 +47,18 @@ public class PixelConversion_Rgba32_To_Argb32 for (nuint i = 0; i < (uint)source.Length; i++) { Rgba32 s = Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); + Unsafe.Add(ref dBase, i) = TPixel.FromRgba32(s); } } [Benchmark] - public void Default_Generic() - { - Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); - } + public void Default_Generic() => Default_GenericImpl(this.source.AsSpan(), this.destination.AsSpan()); [Benchmark] public void Default_Group2() { ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; + ref Argb32 dBase = ref this.destination[0]; for (nuint i = 0; i < (uint)this.Count; i += 2) { @@ -70,8 +66,8 @@ public class PixelConversion_Rgba32_To_Argb32 Rgba32 s1 = Unsafe.Add(ref s0, 1); ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); - d0.FromRgba32(s0); - Unsafe.Add(ref d0, 1).FromRgba32(s1); + d0 = Argb32.FromRgba32(s0); + Unsafe.Add(ref d0, 1) = Argb32.FromRgba32(s1); } } @@ -79,7 +75,7 @@ public class PixelConversion_Rgba32_To_Argb32 public void Default_Group4() { ref Rgba32 sBase = ref this.source[0]; - ref Argb32 dBase = ref this.dest[0]; + ref Argb32 dBase = ref this.destination[0]; for (nuint i = 0; i < (uint)this.Count; i += 4) { @@ -92,10 +88,10 @@ public class PixelConversion_Rgba32_To_Argb32 ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); + d0 = Argb32.FromRgba32(s0); + d1 = Argb32.FromRgba32(s1); + d2 = Argb32.FromRgba32(s2); + Unsafe.Add(ref d2, 1) = Argb32.FromRgba32(s3); } } @@ -103,7 +99,7 @@ public class PixelConversion_Rgba32_To_Argb32 public void BitOps() { ref uint sBase = ref Unsafe.As(ref this.source[0]); - ref uint dBase = ref Unsafe.As(ref this.dest[0]); + ref uint dBase = ref Unsafe.As(ref this.destination[0]); for (nuint i = 0; i < (uint)this.Count; i++) { @@ -116,7 +112,7 @@ public class PixelConversion_Rgba32_To_Argb32 public void BitOps_GroupAsULong() { ref ulong sBase = ref Unsafe.As(ref this.source[0]); - ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + ref ulong dBase = ref Unsafe.As(ref this.destination[0]); for (nuint i = 0; i < (uint)this.Count / 2; i++) { @@ -134,10 +130,6 @@ public class PixelConversion_Rgba32_To_Argb32 public static class FromRgba32 { - /// - /// Converts a packed to . - /// - /// The argb value. [MethodImpl(InliningOptions.ShortMethod)] public static uint ToArgb32(uint packedRgba) { @@ -146,10 +138,6 @@ public class PixelConversion_Rgba32_To_Argb32 return (packedRgba << 8) | (packedRgba >> 24); } - /// - /// Converts a packed to . - /// - /// The bgra value. [MethodImpl(InliningOptions.ShortMethod)] public static uint ToBgra32(uint packedRgba) { diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs index 499f3483d..f4fb9e420 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -55,7 +55,7 @@ public class PixelConversion_Rgba32_To_Bgra32 for (nuint i = 0; i < (uint)this.Count; i++) { ref Rgba32 s = ref Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); + Unsafe.Add(ref dBase, i) = Bgra32.FromRgba32(s); } } @@ -69,15 +69,12 @@ public class PixelConversion_Rgba32_To_Bgra32 for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgba32 s = ref Unsafe.Add(ref sBase, i); - Unsafe.Add(ref dBase, i).FromRgba32(s); + Unsafe.Add(ref dBase, i) = TPixel.FromRgba32(s); } } [Benchmark] - public void Default_Generic() - { - Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); - } + public void Default_Generic() => Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); [Benchmark] public void Default_Group2() @@ -91,8 +88,8 @@ public class PixelConversion_Rgba32_To_Bgra32 Rgba32 s1 = Unsafe.Add(ref s0, 1); ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); - d0.FromRgba32(s0); - Unsafe.Add(ref d0, 1).FromRgba32(s1); + d0 = Bgra32.FromRgba32(s0); + Unsafe.Add(ref d0, 1) = Bgra32.FromRgba32(s1); } } @@ -113,10 +110,10 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); + d0 = Bgra32.FromRgba32(s0); + d1 = Bgra32.FromRgba32(s1); + d2 = Bgra32.FromRgba32(s2); + Unsafe.Add(ref d2, 1) = Bgra32.FromRgba32(s3); } } @@ -138,18 +135,15 @@ public class PixelConversion_Rgba32_To_Bgra32 ref TPixel d1 = ref Unsafe.Add(ref d0, 1); ref TPixel d2 = ref Unsafe.Add(ref d1, 1); - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - Unsafe.Add(ref d2, 1).FromRgba32(s3); + d0 = TPixel.FromRgba32(s0); + d1 = TPixel.FromRgba32(s1); + d2 = TPixel.FromRgba32(s2); + Unsafe.Add(ref d2, 1) = TPixel.FromRgba32(s3); } } // [Benchmark] - public void Default_Group4_Generic() - { - Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); - } + public void Default_Group4_Generic() => Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); // [Benchmark] public void Default_Group8() @@ -178,15 +172,15 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Bgra32 d5 = ref Unsafe.Add(ref d4, 1); ref Bgra32 d6 = ref Unsafe.Add(ref d5, 1); - d0.FromRgba32(s0); - d1.FromRgba32(s1); - d2.FromRgba32(s2); - d3.FromRgba32(s3); + d0 = Bgra32.FromRgba32(s0); + d1 = Bgra32.FromRgba32(s1); + d2 = Bgra32.FromRgba32(s2); + d3 = Bgra32.FromRgba32(s3); - d4.FromRgba32(s4); - d5.FromRgba32(s5); - d6.FromRgba32(s6); - Unsafe.Add(ref d6, 1).FromRgba32(s7); + d4 = Bgra32.FromRgba32(s4); + d5 = Bgra32.FromRgba32(s5); + d6 = Bgra32.FromRgba32(s6); + Unsafe.Add(ref d6, 1) = Bgra32.FromRgba32(s7); } } @@ -352,10 +346,6 @@ public class PixelConversion_Rgba32_To_Bgra32 public static class FromRgba32 { - /// - /// Converts a packed to . - /// - /// The argb value. [MethodImpl(InliningOptions.ShortMethod)] public static uint ToArgb32(uint packedRgba) { @@ -364,10 +354,6 @@ public class PixelConversion_Rgba32_To_Bgra32 return (packedRgba << 8) | (packedRgba >> 24); } - /// - /// Converts a packed to . - /// - /// The bgra value. [MethodImpl(InliningOptions.ShortMethod)] public static uint ToBgra32(uint packedRgba) { diff --git a/tests/ImageSharp.Tests/Color/RgbaDouble.cs b/tests/ImageSharp.Tests/Color/RgbaDouble.cs index fdd736930..bb8672941 100644 --- a/tests/ImageSharp.Tests/Color/RgbaDouble.cs +++ b/tests/ImageSharp.Tests/Color/RgbaDouble.cs @@ -19,41 +19,49 @@ namespace SixLabors.ImageSharp.Tests; /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// -/// -/// Initializes a new instance of the struct. -/// -/// The red component. -/// The green component. -/// The blue component. -/// The alpha component. [StructLayout(LayoutKind.Sequential)] -[method: MethodImpl(InliningOptions.ShortMethod)] -public partial struct RgbaDouble(double r, double g, double b, double a = 1) : IPixel +public struct RgbaDouble : IPixel { /// /// Gets or sets the red component. /// - public double R = r; + public double R; /// /// Gets or sets the green component. /// - public double G = g; + public double G; /// /// Gets or sets the blue component. /// - public double B = b; + public double B; /// /// Gets or sets the alpha component. /// - public double A = a; + public double A; private const float MaxBytes = byte.MaxValue; private static readonly Vector4 Max = new(MaxBytes); private static readonly Vector4 Half = new(0.5F); + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaDouble(double r, double g, double b, double a = 1) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + /// /// Compares two objects for equality. /// @@ -76,6 +84,18 @@ public partial struct RgbaDouble(double r, double g, double b, double a = 1) : I [MethodImpl(InliningOptions.ShortMethod)] public static bool operator !=(RgbaDouble left, RgbaDouble right) => !left.Equals(right); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new((float)this.R, (float)this.G, (float)this.B, (float)this.A); + /// public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( @@ -87,83 +107,68 @@ public partial struct RgbaDouble(double r, double g, double b, double a = 1) : I public readonly PixelOperations CreatePixelOperations() => new(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromVector4(Vector4 source) { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.A = vector.W; + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new(source.X, source.Y, source.Z, source.W); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new((float)this.R, (float)this.G, (float)this.B, (float)this.A); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); /// public override readonly bool Equals(object obj) => obj is RgbaDouble other && this.Equals(other); diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 3e8e17164..c81eaea63 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -26,7 +26,7 @@ public partial class SimdUtilsTests [InlineData(5.3, 536.4, 4.5, 8.1)] public void PseudoRound(float x, float y, float z, float w) { - var v = new Vector4(x, y, z, w); + Vector4 v = new(x, y, z, w); Vector4 actual = v.PseudoRound(); @@ -57,7 +57,7 @@ public partial class SimdUtilsTests { float[] data = new float[Vector.Count]; - var rnd = new Random(seed); + Random rnd = new(seed); for (int i = 0; i < Vector.Count; i++) { @@ -185,10 +185,10 @@ public partial class SimdUtilsTests short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale); float[] fData = sData.Select(u => (float)u).ToArray(); - var source = new Vector(sData); + Vector source = new(sData); - var expected1 = new Vector(fData, 0); - var expected2 = new Vector(fData, n); + Vector expected1 = new(fData, 0); + Vector expected2 = new(fData, n); // Act: SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector actual1, out Vector actual2); @@ -262,7 +262,7 @@ public partial class SimdUtilsTests byte[] g = Enumerable.Range(100, 32).Select(x => (byte)x).ToArray(); byte[] b = Enumerable.Range(200, 32).Select(x => (byte)x).ToArray(); const int padding = 4; - var d = new Rgb24[32 + padding]; + Rgb24[] d = new Rgb24[32 + padding]; ReadOnlySpan rr = r.AsSpan(); ReadOnlySpan gg = g.AsSpan(); @@ -296,7 +296,7 @@ public partial class SimdUtilsTests byte[] g = Enumerable.Range(100, 32).Select(x => (byte)x).ToArray(); byte[] b = Enumerable.Range(200, 32).Select(x => (byte)x).ToArray(); - var d = new Rgba32[32]; + Rgba32[] d = new Rgba32[32]; ReadOnlySpan rr = r.AsSpan(); ReadOnlySpan gg = g.AsSpan(); @@ -322,18 +322,18 @@ public partial class SimdUtilsTests internal static void TestPackFromRgbPlanes(int count, Action packMethod) where TPixel : unmanaged, IPixel { - var rnd = new Random(42); + Random rnd = new(42); byte[] r = rnd.GenerateRandomByteArray(count); byte[] g = rnd.GenerateRandomByteArray(count); byte[] b = rnd.GenerateRandomByteArray(count); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { - expected[i].FromRgb24(new Rgb24(r[i], g[i], b[i])); + expected[i] = TPixel.FromRgb24(new Rgb24(r[i], g[i], b[i])); } - var actual = new TPixel[count + 3]; // padding for Rgb24 AVX2 + TPixel[] actual = new TPixel[count + 3]; // padding for Rgb24 AVX2 packMethod(r, g, b, actual); Assert.True(expected.AsSpan().SequenceEqual(actual.AsSpan()[..count])); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 80365b472..65d0a01ff 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -42,7 +42,7 @@ internal static partial class LibJpegTools public int QuantizationTableIndex => throw new NotSupportedException(); - public Buffer2D SpectralBlocks { get; private set; } + public Buffer2D SpectralBlocks { get; } public short MinVal { get; private set; } = short.MaxValue; @@ -102,7 +102,7 @@ internal static partial class LibJpegTools public static ComponentData Load(JpegComponent c, int index) { - var result = new ComponentData( + ComponentData result = new( c.WidthInBlocks, c.HeightInBlocks, index); @@ -113,7 +113,7 @@ internal static partial class LibJpegTools public Image CreateGrayScaleImage() { - var result = new Image(this.WidthInBlocks * 8, this.HeightInBlocks * 8); + Image result = new(this.WidthInBlocks * 8, this.HeightInBlocks * 8); for (int by = 0; by < this.HeightInBlocks; by++) { @@ -136,9 +136,8 @@ internal static partial class LibJpegTools { float val = this.GetBlockValue(block, x, y); - var v = new Vector4(val, val, val, 1); - Rgba32 color = default; - color.FromVector4(v); + Vector4 v = new(val, val, val, 1); + Rgba32 color = Rgba32.FromVector4(v); int yy = (by * 8) + y; int xx = (bx * 8) + x; @@ -198,7 +197,7 @@ internal static partial class LibJpegTools return false; } - if (object.ReferenceEquals(this, obj)) + if (ReferenceEquals(this, obj)) { return true; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index a4bb6b7bf..9eec547a3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -14,11 +14,11 @@ internal static partial class LibJpegTools /// public class SpectralData : IEquatable { - public int ComponentCount { get; private set; } + public int ComponentCount { get; } - public LibJpegTools.ComponentData[] Components { get; private set; } + public ComponentData[] Components { get; } - internal SpectralData(LibJpegTools.ComponentData[] components) + internal SpectralData(ComponentData[] components) { this.ComponentCount = components.Length; this.Components = components; @@ -31,16 +31,16 @@ internal static partial class LibJpegTools return null; } - LibJpegTools.ComponentData c0 = this.Components[0]; - LibJpegTools.ComponentData c1 = this.Components[1]; - LibJpegTools.ComponentData c2 = this.Components[2]; + ComponentData c0 = this.Components[0]; + ComponentData c1 = this.Components[1]; + ComponentData c2 = this.Components[2]; if (c0.Size != c1.Size || c1.Size != c2.Size) { return null; } - var result = new Image(c0.WidthInBlocks * 8, c0.HeightInBlocks * 8); + Image result = new(c0.WidthInBlocks * 8, c0.HeightInBlocks * 8); for (int by = 0; by < c0.HeightInBlocks; by++) { @@ -55,18 +55,14 @@ internal static partial class LibJpegTools internal void WriteToImage(int bx, int by, Image image) { - LibJpegTools.ComponentData c0 = this.Components[0]; - LibJpegTools.ComponentData c1 = this.Components[1]; - LibJpegTools.ComponentData c2 = this.Components[2]; + ComponentData c0 = this.Components[0]; + ComponentData c1 = this.Components[1]; + ComponentData c2 = this.Components[2]; Block8x8 block0 = c0.SpectralBlocks[bx, by]; Block8x8 block1 = c1.SpectralBlocks[bx, by]; Block8x8 block2 = c2.SpectralBlocks[bx, by]; - float d0 = c0.MaxVal - c0.MinVal; - float d1 = c1.MaxVal - c1.MinVal; - float d2 = c2.MaxVal - c2.MinVal; - for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) @@ -75,9 +71,8 @@ internal static partial class LibJpegTools float val1 = c0.GetBlockValue(block1, x, y); float val2 = c0.GetBlockValue(block2, x, y); - var v = new Vector4(val0, val1, val2, 1); - Rgba32 color = default; - color.FromVector4(v); + Vector4 v = new(val0, val1, val2, 1); + Rgba32 color = Rgba32.FromVector4(v); int yy = (by * 8) + y; int xx = (bx * 8) + x; @@ -105,8 +100,8 @@ internal static partial class LibJpegTools for (int i = 0; i < this.ComponentCount; i++) { - LibJpegTools.ComponentData a = this.Components[i]; - LibJpegTools.ComponentData b = other.Components[i]; + ComponentData a = this.Components[i]; + ComponentData b = other.Components[i]; if (!a.Equals(b)) { return false; @@ -116,10 +111,7 @@ internal static partial class LibJpegTools return true; } - public override bool Equals(object obj) - { - return obj is SpectralData other && this.Equals(other); - } + public override bool Equals(object obj) => obj is SpectralData other && this.Equals(other); public override int GetHashCode() { @@ -139,9 +131,6 @@ internal static partial class LibJpegTools return left.Equals(right); } - public static bool operator !=(SpectralData left, SpectralData right) - { - return !(left == right); - } + public static bool operator !=(SpectralData left, SpectralData right) => !(left == right); } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 825becb36..0fdd49630 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -406,7 +406,7 @@ public partial class PngEncoderTests for (int x = 0; x < image.Width; x++) { - rowSpan[x].FromRgba32(rgba32); + rowSpan[x] = Rgba32.FromRgba32(rgba32); } } }); diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index f0961de6b..b4279b045 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -80,7 +80,7 @@ public class PredictorEncoderTests // Convert image pixels to bgra array. byte[] imgBytes = File.ReadAllBytes(TestImageFullPath(TestImages.Webp.Peak)); - using var image = Image.Load(imgBytes); + using Image image = Image.Load(imgBytes); uint[] bgra = ToBgra(image); const int colorTransformBits = 3; @@ -110,7 +110,7 @@ public class PredictorEncoderTests // Convert image pixels to bgra array. byte[] imgBytes = File.ReadAllBytes(TestImageFullPath(TestImages.Webp.Lossy.BikeSmall)); - using var image = Image.Load(imgBytes); + using Image image = Image.Load(imgBytes); uint[] bgra = ToBgra(image); const int colorTransformBits = 4; @@ -149,10 +149,8 @@ public class PredictorEncoderTests private static Bgra32 ToBgra32(TPixel color) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; - color.ToRgba32(ref rgba); - var bgra = new Bgra32(rgba.R, rgba.G, rgba.B, rgba.A); - return bgra; + Rgba32 rgba = color.ToRgba32(); + return new Bgra32(rgba.R, rgba.G, rgba.B, rgba.A); } private static string TestImageFullPath(string path) diff --git a/tests/ImageSharp.Tests/Issues/Issue594.cs b/tests/ImageSharp.Tests/Issues/Issue594.cs index 51f1ef7c6..23a3913fa 100644 --- a/tests/ImageSharp.Tests/Issues/Issue594.cs +++ b/tests/ImageSharp.Tests/Issues/Issue594.cs @@ -10,8 +10,8 @@ public class Issue594 { // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact(Skip = "Skipped because of issue #594")] - public void NormalizedByte4() + [Fact] // (Skip = "Skipped because of issue #594")] + public void NormalizedByte4Test() { // Test PackedValue Assert.Equal(0x0U, new NormalizedByte4(Vector4.Zero).PackedValue); @@ -33,68 +33,27 @@ public class Issue594 Assert.Equal(0, scaled.W); // Test FromScaledVector4. - var pixel = default(NormalizedByte4); - pixel.FromScaledVector4(scaled); + NormalizedByte4 pixel = NormalizedByte4.FromScaledVector4(scaled); Assert.Equal(0x81818181, pixel.PackedValue); // Test Ordering - float x = 0.1f; - float y = -0.3f; - float z = 0.5f; - float w = -0.7f; - Assert.Equal(0xA740DA0D, new NormalizedByte4(x, y, z, w).PackedValue); - var n = default(NormalizedByte4); - n.FromRgba32(new Rgba32(141, 90, 192, 39)); + const float x = 0.1f; + const float y = -0.3f; + const float z = 0.5f; + const float w = -0.7f; + + pixel = new(x, y, z, w); + Assert.Equal(0xA740DA0D, pixel.PackedValue); + NormalizedByte4 n = NormalizedByte4.FromRgba32(pixel.ToRgba32()); Assert.Equal(0xA740DA0D, n.PackedValue); Assert.Equal(958796544U, new NormalizedByte4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); - - // var rgb = default(Rgb24); - // var rgba = default(Rgba32); - // var bgr = default(Bgr24); - // var bgra = default(Bgra32); - // var argb = default(Argb32); - - // new NormalizedByte4(x, y, z, w).ToRgb24(ref rgb); - // Assert.Equal(rgb, new Rgb24(141, 90, 192)); - - // new NormalizedByte4(x, y, z, w).ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); - - // new NormalizedByte4(x, y, z, w).ToBgr24(ref bgr); - // Assert.Equal(bgr, new Bgr24(141, 90, 192)); - - // new NormalizedByte4(x, y, z, w).ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) - - // new NormalizedByte4(x, y, z, w).ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(141, 90, 192, 39)); - - // http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8 - // var r = default(NormalizedByte4); - // r.FromRgba32(new Rgba32(9, 115, 202, 127)); - // r.ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - - // r.PackedValue = 0xff4af389; - // r.ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - - // r = default(NormalizedByte4); - // r.FromArgb32(new Argb32(9, 115, 202, 127)); - // r.ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(9, 115, 202, 127)); - - // r = default(NormalizedByte4); - // r.FromBgra32(new Bgra32(9, 115, 202, 127)); - // r.ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); } // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact(Skip = "Skipped because of issue #594")] - public void NormalizedShort4() + [Fact] //(Skip = "Skipped because of issue #594")] + public void NormalizedShort4Test() { // Test PackedValue Assert.Equal(0x0UL, new NormalizedShort4(Vector4.Zero).PackedValue); @@ -116,59 +75,22 @@ public class Issue594 Assert.Equal(1, scaled.W); // Test FromScaledVector4. - var pixel = default(NormalizedShort4); - pixel.FromScaledVector4(scaled); + NormalizedShort4 pixel = NormalizedShort4.FromScaledVector4(scaled); Assert.Equal(0x7FFF7FFF7FFF7FFFUL, pixel.PackedValue); // Test Ordering - float x = 0.1f; - float y = -0.3f; - float z = 0.5f; - float w = -0.7f; + const float x = 0.1f; + const float y = -0.3f; + const float z = 0.5f; + const float w = -0.7f; Assert.Equal(0xa6674000d99a0ccd, new NormalizedShort4(x, y, z, w).PackedValue); Assert.Equal(4150390751449251866UL, new NormalizedShort4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); - - // var rgb = default(Rgb24); - // var rgba = default(Rgba32); - // var bgr = default(Bgr24); - // var bgra = default(Bgra32); - // var argb = default(Argb32); - - // new NormalizedShort4(x, y, z, w).ToRgb24(ref rgb); - // Assert.Equal(rgb, new Rgb24(141, 90, 192)); - - // new NormalizedShort4(x, y, z, w).ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) - - // new NormalizedShort4(x, y, z, w).ToBgr24(ref bgr); - // Assert.Equal(bgr, new Bgr24(141, 90, 192)); - - // new NormalizedShort4(x, y, z, w).ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); - - // new NormalizedShort4(x, y, z, w).ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(141, 90, 192, 39)); - - // var r = default(NormalizedShort4); - // r.FromRgba32(new Rgba32(9, 115, 202, 127)); - // r.ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - - // r = default(NormalizedShort4); - // r.FromBgra32(new Bgra32(9, 115, 202, 127)); - // r.ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); - - // r = default(NormalizedShort4); - // r.FromArgb32(new Argb32(9, 115, 202, 127)); - // r.ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(9, 115, 202, 127)); } // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact(Skip = "Skipped because of issue #594")] - public void Short4() + [Fact] // (Skip = "Skipped because of issue #594")] + public void Short4Test() { // Test the limits. Assert.Equal(0x0UL, new Short4(Vector4.Zero).PackedValue); @@ -192,8 +114,7 @@ public class Issue594 Assert.Equal(1, scaled.W); // Test FromScaledVector4. - var pixel = default(Short4); - pixel.FromScaledVector4(scaled); + Short4 pixel = Short4.FromScaledVector4(scaled); Assert.Equal(0x7FFF7FFF7FFF7FFFUL, pixel.PackedValue); // Test clamping. @@ -212,52 +133,11 @@ public class Issue594 z = 29623; w = 193; Assert.Equal(0x00c173b7316d2d1bUL, new Short4(x, y, z, w).PackedValue); - - // var rgb = default(Rgb24); - // var rgba = default(Rgba32); - // var bgr = default(Bgr24); - // var bgra = default(Bgra32); - // var argb = default(Argb32); - - // new Short4(x, y, z, w).ToRgb24(ref rgb); - // Assert.Equal(rgb, new Rgb24(172, 177, 243)); // this assert fails in Release build on linux (#594) - - // new Short4(x, y, z, w).ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(172, 177, 243, 128)); - - // new Short4(x, y, z, w).ToBgr24(ref bgr); - // Assert.Equal(bgr, new Bgr24(172, 177, 243)); - - // new Short4(x, y, z, w).ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(172, 177, 243, 128)); - - // new Short4(x, y, z, w).ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(172, 177, 243, 128)); - - // var r = default(Short4); - // r.FromRgba32(new Rgba32(20, 38, 0, 255)); - // r.ToRgba32(ref rgba); - // Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); - - // r = default(Short4); - // r.FromBgra32(new Bgra32(20, 38, 0, 255)); - // r.ToBgra32(ref bgra); - // Assert.Equal(bgra, new Bgra32(20, 38, 0, 255)); - - // r = default(Short4); - // r.FromArgb32(new Argb32(20, 38, 0, 255)); - // r.ToArgb32(ref argb); - // Assert.Equal(argb, new Argb32(20, 38, 0, 255)); } + // TODO: Use tolerant comparer. // Comparison helpers with small tolerance to allow for floating point rounding during computations. - public static bool Equal(float a, float b) - { - return Math.Abs(a - b) < 1e-5; - } + public static bool Equal(float a, float b) => Math.Abs(a - b) < 1e-5; - public static bool Equal(Vector4 a, Vector4 b) - { - return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z) && Equal(a.W, b.W); - } + public static bool Equal(Vector4 a, Vector4 b) => Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z) && Equal(a.W, b.W); } diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index b7a86d488..4b4e84b4b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -41,12 +41,11 @@ public class A8Tests public void A8_FromScaledVector4() { // Arrange - A8 alpha = default; - int expected = 128; + const int expected = 128; Vector4 scaled = new A8(.5F).ToScaledVector4(); // Act - alpha.FromScaledVector4(scaled); + A8 alpha = A8.FromScaledVector4(scaled); byte actual = alpha.PackedValue; // Assert @@ -91,8 +90,7 @@ public class A8Tests A8 input = new(128); Rgba32 expected = new(0, 0, 0, 128); - Rgba32 actual = default; - input.ToRgba32(ref actual); + Rgba32 actual = input.ToRgba32(); Assert.Equal(expected, actual); } @@ -100,11 +98,10 @@ public class A8Tests public void A8_FromBgra5551() { // arrange - A8 alpha = default; - byte expected = byte.MaxValue; + const byte expected = byte.MaxValue; // act - alpha.FromBgra5551(new Bgra5551(0.0f, 0.0f, 0.0f, 1.0f)); + A8 alpha = A8.FromBgra5551(new Bgra5551(0.0f, 0.0f, 0.0f, 1.0f)); // assert Assert.Equal(expected, alpha.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs index 7f584b1bf..98fdce5db 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -96,8 +96,7 @@ public class Abgr32Tests [Fact] public void FromRgba32() { - Abgr32 abgr = default; - abgr.FromRgba32(new Rgba32(1, 2, 3, 4)); + Abgr32 abgr = Abgr32.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, abgr.R); Assert.Equal(2, abgr.G); @@ -114,8 +113,7 @@ public class Abgr32Tests [Fact] public void FromVector4() { - Abgr32 c = default; - c.FromVector4(Vec(1, 2, 3, 4)); + Abgr32 c = Abgr32.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, c.R); Assert.Equal(2, c.G); @@ -135,11 +133,10 @@ public class Abgr32Tests public void Abgr32_FromBgra5551() { // arrange - Abgr32 abgr = default; - uint expected = uint.MaxValue; + const uint expected = uint.MaxValue; // act - abgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Abgr32 abgr = Abgr32.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, abgr.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index 42b940845..bcaf9265a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -18,8 +18,8 @@ public class Argb32Tests { Argb32 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); Argb32 color2 = new(new Vector4(0.0f)); - Argb32 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - Argb32 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); + Argb32 color3 = new(new Vector4(1f, 0.0f, 1f, 1f)); + Argb32 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -32,9 +32,9 @@ public class Argb32Tests public void AreNotEqual() { Argb32 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - Argb32 color2 = new(new Vector4(1.0f)); - Argb32 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - Argb32 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + Argb32 color2 = new(new Vector4(1f)); + Argb32 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + Argb32 color4 = new(1f, 1f, 0.0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -111,11 +111,10 @@ public class Argb32Tests { // arrange Vector4 scaled = new Argb32(Vector4.One).ToScaledVector4(); - Argb32 pixel = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - pixel.FromScaledVector4(scaled); + Argb32 pixel = Argb32.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -126,11 +125,10 @@ public class Argb32Tests public void Argb32_FromBgra5551() { // arrange - Argb32 argb = default; - uint expected = uint.MaxValue; + const uint expected = uint.MaxValue; // act - argb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Argb32 argb = Argb32.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, argb.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 239e88396..362e20bba 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -80,8 +80,7 @@ public class Bgr24Tests [Fact] public void FromRgba32() { - Bgr24 rgb = default; - rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); + Bgr24 rgb = Bgr24.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -97,8 +96,7 @@ public class Bgr24Tests [Fact] public void FromVector4() { - Bgr24 rgb = default; - rgb.FromVector4(Vec(1, 2, 3, 4)); + Bgr24 rgb = Bgr24.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -116,11 +114,8 @@ public class Bgr24Tests [Fact] public void Bgr24_FromBgra5551() { - // arrange - Bgr24 bgr = default; - // act - bgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Bgr24 bgr = Bgr24.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(255, bgr.R); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index 2ad4f4cf7..3c4a10423 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -84,11 +84,10 @@ public class Bgr565Tests { // arrange Vector4 scaled = new Bgr565(Vector3.One).ToScaledVector4(); - int expected = 0xFFFF; - Bgr565 pixel = default; + const int expected = 0xFFFF; // act - pixel.FromScaledVector4(scaled); + Bgr565 pixel = Bgr565.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -99,11 +98,10 @@ public class Bgr565Tests public void Bgr565_FromBgra5551() { // arrange - Bgr565 bgr = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Bgr565 bgr = Bgr565.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, bgr.PackedValue); @@ -113,14 +111,12 @@ public class Bgr565Tests public void Bgr565_FromArgb32() { // arrange - Bgr565 bgr1 = default; - Bgr565 bgr2 = default; - ushort expected1 = ushort.MaxValue; - ushort expected2 = ushort.MaxValue; + const ushort expected1 = ushort.MaxValue; + const ushort expected2 = ushort.MaxValue; // act - bgr1.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 1.0f)); - bgr2.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 0.0f)); + Bgr565 bgr1 = Bgr565.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 1.0f)); + Bgr565 bgr2 = Bgr565.FromArgb32(new Argb32(1.0f, 1.0f, 1.0f, 0.0f)); // assert Assert.Equal(expected1, bgr1.PackedValue); @@ -131,14 +127,12 @@ public class Bgr565Tests public void Bgr565_FromRgba32() { // arrange - Bgr565 bgr1 = default; - Bgr565 bgr2 = default; - ushort expected1 = ushort.MaxValue; - ushort expected2 = ushort.MaxValue; + const ushort expected1 = ushort.MaxValue; + const ushort expected2 = ushort.MaxValue; // act - bgr1.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 1.0f)); - bgr2.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 0.0f)); + Bgr565 bgr1 = Bgr565.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 1.0f)); + Bgr565 bgr2 = Bgr565.FromRgba32(new Rgba32(1.0f, 1.0f, 1.0f, 0.0f)); // assert Assert.Equal(expected1, bgr1.PackedValue); @@ -149,12 +143,11 @@ public class Bgr565Tests public void Bgr565_ToRgba32() { // arrange - Bgr565 bgra = new(Vector3.One); + Bgr565 pixel = new(Vector3.One); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -163,11 +156,10 @@ public class Bgr565Tests public void Bgra565_FromRgb48() { // arrange - Bgr565 bgr = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgr.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgr565 bgr = Bgr565.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgr.PackedValue); @@ -177,11 +169,10 @@ public class Bgr565Tests public void Bgra565_FromRgba64() { // arrange - Bgr565 bgr = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgr.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgr565 bgr = Bgr565.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgr.PackedValue); @@ -191,11 +182,10 @@ public class Bgr565Tests public void Bgr565_FromBgr24() { // arrange - Bgr565 bgr = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgr.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgr565 bgr = Bgr565.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); @@ -205,11 +195,10 @@ public class Bgr565Tests public void Bgr565_FromRgb24() { // arrange - Bgr565 bgr = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgr.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgr565 bgr = Bgr565.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); @@ -219,11 +208,10 @@ public class Bgr565Tests public void Bgr565_FromGrey8() { // arrange - Bgr565 bgr = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgr.FromL8(new L8(byte.MaxValue)); + Bgr565 bgr = Bgr565.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); @@ -233,11 +221,10 @@ public class Bgr565Tests public void Bgr565_FromGrey16() { // arrange - Bgr565 bgr = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgr.FromL16(new L16(ushort.MaxValue)); + Bgr565 bgr = Bgr565.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index df8d1199f..277975896 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -96,8 +96,7 @@ public class Bgra32Tests [Fact] public void FromRgba32() { - Bgra32 bgra = default; - bgra.FromRgba32(new Rgba32(1, 2, 3, 4)); + Bgra32 bgra = Bgra32.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, bgra.R); Assert.Equal(2, bgra.G); @@ -114,8 +113,7 @@ public class Bgra32Tests [Fact] public void FromVector4() { - Bgra32 c = default; - c.FromVector4(Vec(1, 2, 3, 4)); + Bgra32 c = Bgra32.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, c.R); Assert.Equal(2, c.G); @@ -135,11 +133,10 @@ public class Bgra32Tests public void Bgra32_FromBgra5551() { // arrange - Bgra32 bgra = default; - uint expected = uint.MaxValue; + const uint expected = uint.MaxValue; // act - bgra.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Bgra32 bgra = Bgra32.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, bgra.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index ef587f301..5d20b5cf1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -67,10 +67,10 @@ public class Bgra4444Tests public void Bgra4444_ToScaledVector4() { // arrange - Bgra4444 bgra = new(Vector4.One); + Bgra4444 pixel = new(Vector4.One); // act - Vector4 actual = bgra.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -83,12 +83,11 @@ public class Bgra4444Tests public void Bgra4444_ToRgba32() { // arrange - Bgra4444 bgra = new(Vector4.One); + Bgra4444 pixel = new(Vector4.One); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -98,12 +97,11 @@ public class Bgra4444Tests { // arrange Vector4 scaled = new Bgra4444(Vector4.One).ToScaledVector4(); - int expected = 0xFFFF; - Bgra4444 bgra = default; + const int expected = 0xFFFF; // act - bgra.FromScaledVector4(scaled); - ushort actual = bgra.PackedValue; + Bgra4444 pixel = Bgra4444.FromScaledVector4(scaled); + ushort actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); @@ -113,42 +111,38 @@ public class Bgra4444Tests public void Bgra4444_FromBgra5551() { // arrange - Bgra4444 bgra = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - bgra.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Bgra4444 pixel = Bgra4444.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert - Assert.Equal(expected, bgra.PackedValue); + Assert.Equal(expected, pixel.PackedValue); } [Fact] public void Bgra4444_FromArgb32() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromArgb32(new Argb32(255, 255, 255, 255)); + Bgra4444 pixel = Bgra4444.FromArgb32(new Argb32(255, 255, 255, 255)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromRgba32() { // arrange - Bgra4444 bgra1 = default; - Bgra4444 bgra2 = default; - ushort expectedPackedValue1 = ushort.MaxValue; - ushort expectedPackedValue2 = 0xFF0F; + const ushort expectedPackedValue1 = ushort.MaxValue; + const ushort expectedPackedValue2 = 0xFF0F; // act - bgra1.FromRgba32(new Rgba32(255, 255, 255, 255)); - bgra2.FromRgba32(new Rgba32(255, 0, 255, 255)); + Bgra4444 bgra1 = Bgra4444.FromRgba32(new Rgba32(255, 255, 255, 255)); + Bgra4444 bgra2 = Bgra4444.FromRgba32(new Rgba32(255, 0, 255, 255)); // assert Assert.Equal(expectedPackedValue1, bgra1.PackedValue); @@ -159,84 +153,78 @@ public class Bgra4444Tests public void Bgra4444_FromRgb48() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgra4444 pixel = Bgra4444.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromRgba64() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgra4444 pixel = Bgra4444.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromGrey16() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromL16(new L16(ushort.MaxValue)); + Bgra4444 pixel = Bgra4444.FromL16(new L16(ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromGrey8() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromL8(new L8(byte.MaxValue)); + Bgra4444 pixel = Bgra4444.FromL8(new L8(byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromBgr24() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgra4444 pixel = Bgra4444.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra4444_FromRgb24() { // arrange - Bgra4444 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgra4444 pixel = Bgra4444.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index 5140cfda1..38f809e49 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -18,8 +18,8 @@ public class Bgra5551Tests { Bgra5551 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); Bgra5551 color2 = new(new Vector4(0.0f)); - Bgra5551 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - Bgra5551 color4 = new(1.0f, 0.0f, 0.0f, 1.0f); + Bgra5551 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + Bgra5551 color4 = new(1f, 0.0f, 0.0f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -32,9 +32,9 @@ public class Bgra5551Tests public void AreNotEqual() { Bgra5551 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - Bgra5551 color2 = new(new Vector4(1.0f)); - Bgra5551 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - Bgra5551 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + Bgra5551 color2 = new(new Vector4(1f)); + Bgra5551 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + Bgra5551 color4 = new(1f, 1f, 0.0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -43,10 +43,10 @@ public class Bgra5551Tests [Fact] public void Bgra5551_PackedValue() { - float x = 0x1a; - float y = 0x16; - float z = 0xd; - float w = 0x1; + const float x = 0x1a; + const float y = 0x16; + const float z = 0xd; + const float w = 0x1; Assert.Equal(0xeacd, new Bgra5551(x / 0x1f, y / 0x1f, z / 0x1f, w).PackedValue); Assert.Equal(3088, new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f).PackedValue); @@ -72,10 +72,10 @@ public class Bgra5551Tests public void Bgra5551_ToScaledVector4() { // arrange - Bgra5551 bgra = new(Vector4.One); + Bgra5551 pixel = new(Vector4.One); // act - Vector4 actual = bgra.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -88,12 +88,11 @@ public class Bgra5551Tests public void Bgra5551_ToRgba32() { // arrange - Bgra5551 bgra = new(Vector4.One); + Bgra5551 pixel = new(Vector4.One); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -103,11 +102,10 @@ public class Bgra5551Tests { // arrange Vector4 scaled = new Bgra5551(Vector4.One).ToScaledVector4(); - int expected = 0xFFFF; - Bgra5551 pixel = default; + const int expected = 0xFFFF; // act - pixel.FromScaledVector4(scaled); + Bgra5551 pixel = Bgra5551.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -118,13 +116,11 @@ public class Bgra5551Tests public void Bgra5551_FromBgra5551() { // arrange - Bgra5551 bgra = default; - Bgra5551 actual = default; - Bgra5551 expected = new(1.0f, 0.0f, 1.0f, 1.0f); + Bgra5551 expected = new(1f, 0.0f, 1f, 1f); // act - bgra.FromBgra5551(expected); - actual.FromBgra5551(bgra); + Bgra5551 pixel = Bgra5551.FromBgra5551(expected); + Bgra5551 actual = Bgra5551.FromBgra5551(pixel); // assert Assert.Equal(expected, actual); @@ -134,14 +130,12 @@ public class Bgra5551Tests public void Bgra5551_FromRgba32() { // arrange - Bgra5551 bgra1 = default; - Bgra5551 bgra2 = default; - ushort expectedPackedValue1 = ushort.MaxValue; - ushort expectedPackedValue2 = 0xFC1F; + const ushort expectedPackedValue1 = ushort.MaxValue; + const ushort expectedPackedValue2 = 0xFC1F; // act - bgra1.FromRgba32(new Rgba32(255, 255, 255, 255)); - bgra2.FromRgba32(new Rgba32(255, 0, 255, 255)); + Bgra5551 bgra1 = Bgra5551.FromRgba32(new Rgba32(255, 255, 255, 255)); + Bgra5551 bgra2 = Bgra5551.FromRgba32(new Rgba32(255, 0, 255, 255)); // assert Assert.Equal(expectedPackedValue1, bgra1.PackedValue); @@ -152,14 +146,12 @@ public class Bgra5551Tests public void Bgra5551_FromBgra32() { // arrange - Bgra5551 bgra1 = default; - Bgra5551 bgra2 = default; - ushort expectedPackedValue1 = ushort.MaxValue; - ushort expectedPackedValue2 = 0xFC1F; + const ushort expectedPackedValue1 = ushort.MaxValue; + const ushort expectedPackedValue2 = 0xFC1F; // act - bgra1.FromBgra32(new Bgra32(255, 255, 255, 255)); - bgra2.FromBgra32(new Bgra32(255, 0, 255, 255)); + Bgra5551 bgra1 = Bgra5551.FromBgra32(new Bgra32(255, 255, 255, 255)); + Bgra5551 bgra2 = Bgra5551.FromBgra32(new Bgra32(255, 0, 255, 255)); // assert Assert.Equal(expectedPackedValue1, bgra1.PackedValue); @@ -170,99 +162,91 @@ public class Bgra5551Tests public void Bgra5551_FromArgb32() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromArgb32(new Argb32(255, 255, 255, 255)); + Bgra5551 pixel = Bgra5551.FromArgb32(new Argb32(255, 255, 255, 255)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromRgb48() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgra5551 pixel = Bgra5551.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromRgba64() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Bgra5551 pixel = Bgra5551.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromGrey16() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromL16(new L16(ushort.MaxValue)); + Bgra5551 pixel = Bgra5551.FromL16(new L16(ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromGrey8() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromL8(new L8(byte.MaxValue)); + Bgra5551 pixel = Bgra5551.FromL8(new L8(byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromBgr24() { // arrange - Bgra5551 bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgra5551 pixel = Bgra5551.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Bgra5551_FromRgb24() { // arrange - Bgra5551 - bgra = default; - ushort expectedPackedValue = ushort.MaxValue; + const ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Bgra5551 pixel = Bgra5551.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, bgra.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index a61318db7..e73d64640 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -16,10 +16,10 @@ public class Byte4Tests [Fact] public void AreEqual() { - Byte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - Byte4 color2 = new(new Vector4(0.0f)); - Byte4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - Byte4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); + Byte4 color1 = new(0f, 0f, 0f, 0f); + Byte4 color2 = new(new Vector4(0f)); + Byte4 color3 = new(new Vector4(1f, 0f, 1f, 1f)); + Byte4 color4 = new(1f, 0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -31,10 +31,10 @@ public class Byte4Tests [Fact] public void AreNotEqual() { - Byte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - Byte4 color2 = new(new Vector4(1.0f)); - Byte4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - Byte4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + Byte4 color1 = new(0f, 0f, 0f, 0f); + Byte4 color2 = new(new Vector4(1f)); + Byte4 color3 = new(new Vector4(1f, 0f, 0f, 1f)); + Byte4 color4 = new(1f, 1f, 0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -64,10 +64,10 @@ public class Byte4Tests public void Byte4_ToScaledVector4() { // arrange - Byte4 byte4 = new(Vector4.One * 255); + Byte4 pixel = new(Vector4.One * 255); // act - Vector4 actual = byte4.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -80,12 +80,11 @@ public class Byte4Tests public void Byte4_ToRgba32() { // arrange - Byte4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Byte4 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -95,11 +94,10 @@ public class Byte4Tests { // arrange Vector4 scaled = new Byte4(Vector4.One * 255).ToScaledVector4(); - Byte4 pixel = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - pixel.FromScaledVector4(scaled); + Byte4 pixel = Byte4.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -110,126 +108,117 @@ public class Byte4Tests public void Byte4_FromArgb32() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + Byte4 pixel = Byte4.FromArgb32(new Argb32(255, 255, 255, 255)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromBgr24() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Byte4 pixel = Byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromGrey8() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromL8(new L8(byte.MaxValue)); + Byte4 pixel = Byte4.FromL8(new L8(byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromGrey16() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromL16(new L16(ushort.MaxValue)); + Byte4 pixel = Byte4.FromL16(new L16(ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromRgb24() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Byte4 pixel = Byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromBgra5551() { // arrange - Byte4 byte4 = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - byte4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Byte4 pixel = Byte4.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert - Assert.Equal(expected, byte4.PackedValue); + Assert.Equal(expected, pixel.PackedValue); } [Fact] public void Byte4_FromRgba32() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue1 = uint.MaxValue; + const uint expectedPackedValue1 = uint.MaxValue; // act - byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + Byte4 pixel = Byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); // assert - Assert.Equal(expectedPackedValue1, byte4.PackedValue); + Assert.Equal(expectedPackedValue1, pixel.PackedValue); } [Fact] public void Byte4_FromRgb48() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Byte4 pixel = Byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] public void Byte4_FromRgba64() { // arrange - Byte4 byte4 = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Byte4 pixel = Byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expectedPackedValue, byte4.PackedValue); + Assert.Equal(expectedPackedValue, pixel.PackedValue); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index 3e84da723..9366c51c9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -26,11 +26,11 @@ public class HalfSingleTests public void HalfSingle_ToVector4() { // arrange - HalfSingle halfSingle = new(0.5f); + HalfSingle pixel = new(0.5f); Vector4 expected = new(0.5f, 0, 0, 1); // act - Vector4 actual = halfSingle.ToVector4(); + Vector4 actual = pixel.ToVector4(); // assert Assert.Equal(expected, actual); @@ -40,10 +40,10 @@ public class HalfSingleTests public void HalfSingle_ToScaledVector4() { // arrange - HalfSingle halfSingle = new(-1F); + HalfSingle pixel = new(-1F); // act - Vector4 actual = halfSingle.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(0, actual.X); @@ -57,12 +57,11 @@ public class HalfSingleTests { // arrange Vector4 scaled = new HalfSingle(-1F).ToScaledVector4(); - int expected = 48128; - HalfSingle halfSingle = default; + const int expected = 48128; // act - halfSingle.FromScaledVector4(scaled); - ushort actual = halfSingle.PackedValue; + HalfSingle pixel = HalfSingle.FromScaledVector4(scaled); + ushort actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index 04e3ee35f..c5a89df1e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -48,11 +48,10 @@ public class HalfVector2Tests { // arrange Vector4 scaled = new HalfVector2(Vector2.One).ToScaledVector4(); - uint expected = 1006648320u; - HalfVector2 halfVector = default; + const uint expected = 1006648320u; // act - halfVector.FromScaledVector4(scaled); + HalfVector2 halfVector = HalfVector2.FromScaledVector4(scaled); uint actual = halfVector.PackedValue; // assert @@ -76,14 +75,11 @@ public class HalfVector2Tests [Fact] public void HalfVector2_FromBgra5551() { - // arrange - HalfVector2 halfVector2 = default; - // act - halfVector2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + HalfVector2 pixel = HalfVector2.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert - Vector4 actual = halfVector2.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); Assert.Equal(1F, actual.X); Assert.Equal(1F, actual.Y); Assert.Equal(0, actual.Z); diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index e8420ddd0..16c78a23d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -39,10 +39,10 @@ public class HalfVector4Tests public void HalfVector4_ToScaledVector4() { // arrange - HalfVector4 halfVector4 = new(-Vector4.One); + HalfVector4 pixel = new(-Vector4.One); // act - Vector4 actual = halfVector4.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(0, actual.X); @@ -55,13 +55,12 @@ public class HalfVector4Tests public void HalfVector4_FromScaledVector4() { // arrange - HalfVector4 halfVector4 = default; Vector4 scaled = new HalfVector4(-Vector4.One).ToScaledVector4(); - ulong expected = 13547034390470638592uL; + const ulong expected = 13547034390470638592uL; // act - halfVector4.FromScaledVector4(scaled); - ulong actual = halfVector4.PackedValue; + HalfVector4 pixel = HalfVector4.FromScaledVector4(scaled); + ulong actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); @@ -71,14 +70,13 @@ public class HalfVector4Tests public void HalfVector4_FromBgra5551() { // arrange - HalfVector4 halfVector4 = default; Vector4 expected = Vector4.One; // act - halfVector4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + HalfVector4 pixel = HalfVector4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert - Assert.Equal(expected, halfVector4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 447433dab..2ddf1accb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -40,13 +40,12 @@ public class L16Tests public void L16_FromScaledVector4() { // Arrange - L16 gray = default; const ushort expected = 32767; Vector4 scaled = new L16(expected).ToScaledVector4(); // Act - gray.FromScaledVector4(scaled); - ushort actual = gray.PackedValue; + L16 pixel = L16.FromScaledVector4(scaled); + ushort actual = pixel.PackedValue; // Assert Assert.Equal(expected, actual); @@ -59,10 +58,10 @@ public class L16Tests public void L16_ToScaledVector4(ushort input) { // Arrange - L16 gray = new(input); + L16 pixel = new(input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float vectorInput = input / 65535F; @@ -76,13 +75,12 @@ public class L16Tests public void L16_FromVector4() { // Arrange - L16 gray = default; const ushort expected = 32767; Vector4 vector = new L16(expected).ToVector4(); // Act - gray.FromVector4(vector); - ushort actual = gray.PackedValue; + L16 pixel = L16.FromVector4(vector); + ushort actual = pixel.PackedValue; // Assert Assert.Equal(expected, actual); @@ -95,10 +93,10 @@ public class L16Tests public void L16_ToVector4(ushort input) { // Arrange - L16 gray = new(input); + L16 pixel = new(input); // Act - Vector4 actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -112,14 +110,13 @@ public class L16Tests public void L16_FromRgba32() { // Arrange - L16 gray = default; const byte rgb = 128; ushort scaledRgb = ColorNumerics.From8BitTo16Bit(rgb); ushort expected = ColorNumerics.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act - gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); - ushort actual = gray.PackedValue; + L16 pixel = L16.FromRgba32(new Rgba32(rgb, rgb, rgb)); + ushort actual = pixel.PackedValue; // Assert Assert.Equal(expected, actual); @@ -133,11 +130,10 @@ public class L16Tests { // Arrange ushort expected = ColorNumerics.From16BitTo8Bit(input); - L16 gray = new(input); + L16 pixel = new(input); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(expected, actual.R); @@ -150,14 +146,13 @@ public class L16Tests public void L16_FromBgra5551() { // arrange - L16 gray = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - gray.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + L16 pixel = L16.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert - Assert.Equal(expected, gray.PackedValue); + Assert.Equal(expected, pixel.PackedValue); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index 81f4d61fb..40c746cf2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -44,13 +44,12 @@ public class L8Tests public void L8_FromScaledVector4() { // Arrange - L8 gray = default; const byte expected = 128; Vector4 scaled = new L8(expected).ToScaledVector4(); // Act - gray.FromScaledVector4(scaled); - byte actual = gray.PackedValue; + L8 pixel = L8.FromScaledVector4(scaled); + byte actual = pixel.PackedValue; // Assert Assert.Equal(expected, actual); @@ -61,10 +60,10 @@ public class L8Tests public void L8_ToScaledVector4(byte input) { // Arrange - L8 gray = new(input); + L8 pixel = new(input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float scaledInput = input / 255F; @@ -79,12 +78,11 @@ public class L8Tests public void L8_FromVector4(byte luminance) { // Arrange - L8 gray = default; Vector4 vector = new L8(luminance).ToVector4(); // Act - gray.FromVector4(vector); - byte actual = gray.PackedValue; + L8 pixel = L8.FromVector4(vector); + byte actual = pixel.PackedValue; // Assert Assert.Equal(luminance, actual); @@ -95,10 +93,10 @@ public class L8Tests public void L8_ToVector4(byte input) { // Arrange - L8 gray = new(input); + L8 pixel = new(input); // Act - Vector4 actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float scaledInput = input / 255F; @@ -113,12 +111,11 @@ public class L8Tests public void L8_FromRgba32(byte rgb) { // Arrange - L8 gray = default; byte expected = ColorNumerics.Get8BitBT709Luminance(rgb, rgb, rgb); // Act - gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); - byte actual = gray.PackedValue; + L8 pixel = L8.FromRgba32(new Rgba32(rgb, rgb, rgb)); + byte actual = pixel.PackedValue; // Assert Assert.Equal(expected, actual); @@ -129,11 +126,10 @@ public class L8Tests public void L8_ToRgba32(byte luminance) { // Arrange - L8 gray = new(luminance); + L8 pixel = new(luminance); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(luminance, actual.R); @@ -146,11 +142,10 @@ public class L8Tests public void L8_FromBgra5551() { // arrange - L8 grey = default; - byte expected = byte.MaxValue; + const byte expected = byte.MaxValue; // act - grey.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + L8 grey = L8.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, grey.PackedValue); @@ -167,11 +162,9 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); - L8 mirror = default; - mirror.FromRgba32(rgba); + L8 mirror = L8.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -182,11 +175,8 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); - - L8 mirror = default; - mirror.FromRgba32(rgba); + Rgba32 rgba = original.ToRgba32(); + L8 mirror = L8.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -197,11 +187,10 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); Vector4 l8Vector = original.ToVector4(); - Vector4 rgbaVector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); Assert.Equal(l8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -212,13 +201,11 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); - Vector4 rgbaVector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); - L8 mirror = default; - mirror.FromVector4(rgbaVector); + L8 mirror = L8.FromVector4(rgbaVector); Assert.Equal(original, mirror); } @@ -229,8 +216,7 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); Vector4 l8Vector = original.ToScaledVector4(); Vector4 rgbaVector = original.ToScaledVector4(); @@ -244,13 +230,11 @@ public class L8Tests { L8 original = new(luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); - Vector4 rgbaVector = original.ToScaledVector4(); + Vector4 rgbaVector = rgba.ToScaledVector4(); - L8 mirror = default; - mirror.FromScaledVector4(rgbaVector); + L8 mirror = L8.FromScaledVector4(rgbaVector); Assert.Equal(original, mirror); } diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index 612d0f81a..a18b31f6b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -44,12 +44,11 @@ public class La16Tests public void La16_FromScaledVector4() { // Arrange - La16 gray = default; const ushort expected = 32896; Vector4 scaled = new La16(128, 128).ToScaledVector4(); // Act - gray.FromScaledVector4(scaled); + La16 gray = La16.FromScaledVector4(scaled); ushort actual = gray.PackedValue; // Assert @@ -79,11 +78,10 @@ public class La16Tests public void La16_FromVector4(byte luminance) { // Arrange - La16 gray = default; Vector4 vector = new La16(luminance, luminance).ToVector4(); // Act - gray.FromVector4(vector); + La16 gray = La16.FromVector4(vector); byte actualL = gray.L; byte actualA = gray.A; @@ -115,11 +113,10 @@ public class La16Tests public void La16_FromRgba32(byte rgb) { // Arrange - La16 gray = default; byte expected = ColorNumerics.Get8BitBT709Luminance(rgb, rgb, rgb); // Act - gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); + La16 gray = La16.FromRgba32(new Rgba32(rgb, rgb, rgb)); byte actual = gray.L; // Assert @@ -135,8 +132,7 @@ public class La16Tests La16 gray = new(luminance, luminance); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = gray.ToRgba32(); // Assert Assert.Equal(luminance, actual.R); @@ -149,11 +145,10 @@ public class La16Tests public void La16_FromBgra5551() { // arrange - La16 grey = default; - byte expected = byte.MaxValue; + const byte expected = byte.MaxValue; // act - grey.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + La16 grey = La16.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, grey.L); @@ -171,11 +166,9 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); - La16 mirror = default; - mirror.FromRgba32(rgba); + La16 mirror = La16.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -186,11 +179,9 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); - La16 mirror = default; - mirror.FromRgba32(rgba); + La16 mirror = La16.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -201,11 +192,10 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); Vector4 la16Vector = original.ToVector4(); - Vector4 rgbaVector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); Assert.Equal(la16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -216,13 +206,10 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); + Vector4 rgbaVector = rgba.ToVector4(); - Vector4 rgbaVector = original.ToVector4(); - - La16 mirror = default; - mirror.FromVector4(rgbaVector); + La16 mirror = La16.FromVector4(rgbaVector); Assert.Equal(original, mirror); } @@ -233,11 +220,10 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + Rgba32 rgba = original.ToRgba32(); Vector4 la16Vector = original.ToScaledVector4(); - Vector4 rgbaVector = original.ToScaledVector4(); + Vector4 rgbaVector = rgba.ToScaledVector4(); Assert.Equal(la16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -248,13 +234,10 @@ public class La16Tests { La16 original = new(luminance, luminance); - Rgba32 rgba = default; - original.ToRgba32(ref rgba); - - Vector4 rgbaVector = original.ToScaledVector4(); + Rgba32 rgba = original.ToRgba32(); + Vector4 rgbaVector = rgba.ToScaledVector4(); - La16 mirror = default; - mirror.FromScaledVector4(rgbaVector); + La16 mirror = La16.FromScaledVector4(rgbaVector); Assert.Equal(original, mirror); } diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index 57b70f784..3c702419d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -40,14 +40,13 @@ public class La32Tests public void La32_FromScaledVector4() { // Arrange - La32 gray = default; const ushort expected = 32767; Vector4 scaled = new La32(expected, expected).ToScaledVector4(); // Act - gray.FromScaledVector4(scaled); - ushort actual = gray.L; - ushort actualA = gray.A; + La32 pixel = La32.FromScaledVector4(scaled); + ushort actual = pixel.L; + ushort actualA = pixel.A; // Assert Assert.Equal(expected, actual); @@ -61,10 +60,10 @@ public class La32Tests public void La32_ToScaledVector4(ushort input) { // Arrange - La32 gray = new(input, input); + La32 pixel = new(input, input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float vectorInput = input / 65535F; @@ -78,14 +77,13 @@ public class La32Tests public void La32_FromVector4() { // Arrange - La32 gray = default; const ushort expected = 32767; Vector4 vector = new La32(expected, expected).ToVector4(); // Act - gray.FromVector4(vector); - ushort actual = gray.L; - ushort actualA = gray.A; + La32 pixel = La32.FromVector4(vector); + ushort actual = pixel.L; + ushort actualA = pixel.A; // Assert Assert.Equal(expected, actual); @@ -99,10 +97,10 @@ public class La32Tests public void La32_ToVector4(ushort input) { // Arrange - La32 gray = new(input, input); + La32 pixel = new(input, input); // Act - Vector4 actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -116,18 +114,17 @@ public class La32Tests public void La32_FromRgba32() { // Arrange - La32 gray = default; const byte rgb = 128; ushort scaledRgb = ColorNumerics.From8BitTo16Bit(rgb); ushort expected = ColorNumerics.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act - gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); - ushort actual = gray.L; + La32 pixel = La32.FromRgba32(new Rgba32(rgb, rgb, rgb)); + ushort actual = pixel.L; // Assert Assert.Equal(expected, actual); - Assert.Equal(ushort.MaxValue, gray.A); + Assert.Equal(ushort.MaxValue, pixel.A); } [Theory] @@ -138,11 +135,10 @@ public class La32Tests { // Arrange ushort expected = ColorNumerics.From16BitTo8Bit(input); - La32 gray = new(input, ushort.MaxValue); + La32 pixel = new(input, ushort.MaxValue); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(expected, actual.R); @@ -155,15 +151,14 @@ public class La32Tests public void La32_FromBgra5551() { // arrange - La32 gray = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - gray.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + La32 pixel = La32.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert - Assert.Equal(expected, gray.L); - Assert.Equal(expected, gray.A); + Assert.Equal(expected, pixel.L); + Assert.Equal(expected, pixel.A); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 89c0ae69f..ffbddb139 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -40,10 +40,10 @@ public class NormalizedByte2Tests public void NormalizedByte2_ToScaledVector4() { // arrange - NormalizedByte2 byte2 = new(-Vector2.One); + NormalizedByte2 pixel = new(-Vector2.One); // act - Vector4 actual = byte2.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(0, actual.X); @@ -57,12 +57,11 @@ public class NormalizedByte2Tests { // arrange Vector4 scaled = new NormalizedByte2(-Vector2.One).ToScaledVector4(); - NormalizedByte2 byte2 = default; - uint expected = 0x8181; + const uint expected = 0x8181; // act - byte2.FromScaledVector4(scaled); - uint actual = byte2.PackedValue; + NormalizedByte2 pixel = NormalizedByte2.FromScaledVector4(scaled); + uint actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); @@ -72,14 +71,13 @@ public class NormalizedByte2Tests public void NormalizedByte2_FromBgra5551() { // arrange - NormalizedByte2 normalizedByte2 = default; Vector4 expected = new(1, 1, 0, 1); // act - normalizedByte2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + NormalizedByte2 pixel = NormalizedByte2.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert - Assert.Equal(expected, normalizedByte2.ToVector4()); + Assert.Equal(expected, pixel.ToVector4()); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 297438c65..5a025f6c4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -18,8 +18,8 @@ public class NormalizedByte4Tests { NormalizedByte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); NormalizedByte4 color2 = new(new Vector4(0.0f)); - NormalizedByte4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - NormalizedByte4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); + NormalizedByte4 color3 = new(new Vector4(1f, 0.0f, 1f, 1f)); + NormalizedByte4 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -32,9 +32,9 @@ public class NormalizedByte4Tests public void AreNotEqual() { NormalizedByte4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - NormalizedByte4 color2 = new(new Vector4(1.0f)); - NormalizedByte4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - NormalizedByte4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + NormalizedByte4 color2 = new(new Vector4(1f)); + NormalizedByte4 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + NormalizedByte4 color4 = new(1f, 1f, 0.0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -80,12 +80,11 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromScaledVector4() { // arrange - NormalizedByte4 pixel = default; Vector4 scaled = new NormalizedByte4(-Vector4.One).ToScaledVector4(); - uint expected = 0x81818181; + const uint expected = 0x81818181; // act - pixel.FromScaledVector4(scaled); + NormalizedByte4 pixel = NormalizedByte4.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -96,138 +95,128 @@ public class NormalizedByte4Tests public void NormalizedByte4_FromArgb32() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + NormalizedByte4 pixel = NormalizedByte4.FromArgb32(new Argb32(255, 255, 255, 255)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromBgr24() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromGrey8() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromL8(new L8(byte.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromL8(new L8(byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromGrey16() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromL16(new L16(ushort.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromL16(new L16(ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromRgb24() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromRgba32() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + NormalizedByte4 pixel = NormalizedByte4.FromRgba32(new Rgba32(255, 255, 255, 255)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromBgra5551() { // arrange - NormalizedByte4 normalizedByte4 = default; Vector4 expected = Vector4.One; // act - normalizedByte4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + NormalizedByte4 pixel = NormalizedByte4.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert - Assert.Equal(expected, normalizedByte4.ToVector4()); + Assert.Equal(expected, pixel.ToVector4()); } [Fact] public void NormalizedByte4_FromRgb48() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_FromRgba64() { // arrange - NormalizedByte4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + NormalizedByte4 pixel = NormalizedByte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedByte4_ToRgba32() { // arrange - NormalizedByte4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + NormalizedByte4 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index 4b912d866..be7b39052 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -61,11 +61,10 @@ public class NormalizedShort2Tests { // arrange Vector4 scaled = new NormalizedShort2(-Vector2.One).ToScaledVector4(); - NormalizedShort2 short2 = default; - uint expected = 0x80018001; + const uint expected = 0x80018001; // act - short2.FromScaledVector4(scaled); + NormalizedShort2 short2 = NormalizedShort2.FromScaledVector4(scaled); uint actual = short2.PackedValue; // assert @@ -76,11 +75,10 @@ public class NormalizedShort2Tests public void NormalizedShort2_FromBgra5551() { // arrange - NormalizedShort2 normalizedShort2 = default; Vector4 expected = new(1, 1, 0, 1); // act - normalizedShort2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + NormalizedShort2 normalizedShort2 = NormalizedShort2.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, normalizedShort2.ToVector4()); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index 0d9765888..281ae7ee5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -18,8 +18,8 @@ public class NormalizedShort4Tests { NormalizedShort4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); NormalizedShort4 color2 = new(new Vector4(0.0f)); - NormalizedShort4 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - NormalizedShort4 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); + NormalizedShort4 color3 = new(new Vector4(1f, 0.0f, 1f, 1f)); + NormalizedShort4 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -32,9 +32,9 @@ public class NormalizedShort4Tests public void AreNotEqual() { NormalizedShort4 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - NormalizedShort4 color2 = new(new Vector4(1.0f)); - NormalizedShort4 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - NormalizedShort4 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + NormalizedShort4 color2 = new(new Vector4(1f)); + NormalizedShort4 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + NormalizedShort4 color4 = new(1f, 1f, 0.0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -81,12 +81,11 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromScaledVector4() { // arrange - NormalizedShort4 pixel = default; Vector4 scaled = new NormalizedShort4(Vector4.One).ToScaledVector4(); - ulong expected = 0x7FFF7FFF7FFF7FFF; + const ulong expected = 0x7FFF7FFF7FFF7FFF; // act - pixel.FromScaledVector4(scaled); + NormalizedShort4 pixel = NormalizedShort4.FromScaledVector4(scaled); ulong actual = pixel.PackedValue; // assert @@ -97,124 +96,115 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromArgb32() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromArgb32(new Argb32(255, 255, 255, 255)); + NormalizedShort4 pixel = NormalizedShort4.FromArgb32(new Argb32(255, 255, 255, 255)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromBgr24() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromGrey8() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromL8(new L8(byte.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromL8(new L8(byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromGrey16() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromL16(new L16(ushort.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromL16(new L16(ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromRgb24() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromRgba32() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgba32(new Rgba32(255, 255, 255, 255)); + NormalizedShort4 pixel = NormalizedShort4.FromRgba32(new Rgba32(255, 255, 255, 255)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromRgb48() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_FromRgba64() { // arrange - NormalizedShort4 byte4 = default; Vector4 expected = Vector4.One; // act - byte4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + NormalizedShort4 pixel = NormalizedShort4.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert - Assert.Equal(expected, byte4.ToScaledVector4()); + Assert.Equal(expected, pixel.ToScaledVector4()); } [Fact] public void NormalizedShort4_ToRgba32() { // arrange - NormalizedShort4 byte4 = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + NormalizedShort4 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); Rgba32 expected = new(Vector4.One); - Rgba32 actual = default; // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -223,11 +213,10 @@ public class NormalizedShort4Tests public void NormalizedShort4_FromBgra5551() { // arrange - NormalizedShort4 normalizedShort4 = default; Vector4 expected = Vector4.One; // act - normalizedShort4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + NormalizedShort4 normalizedShort4 = NormalizedShort4.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, normalizedShort4.ToVector4()); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index 413492281..2a5c5765a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -14,7 +14,7 @@ public abstract partial class PixelConverterTests { public static byte[] MakeRgba32ByteArray(byte r, byte g, byte b, byte a) { - var buffer = new byte[256]; + byte[] buffer = new byte[256]; for (int i = 0; i < buffer.Length; i += 4) { @@ -29,7 +29,7 @@ public abstract partial class PixelConverterTests public static byte[] MakeArgb32ByteArray(byte r, byte g, byte b, byte a) { - var buffer = new byte[256]; + byte[] buffer = new byte[256]; for (int i = 0; i < buffer.Length; i += 4) { @@ -44,7 +44,7 @@ public abstract partial class PixelConverterTests public static byte[] MakeBgra32ByteArray(byte r, byte g, byte b, byte a) { - var buffer = new byte[256]; + byte[] buffer = new byte[256]; for (int i = 0; i < buffer.Length; i += 4) { @@ -59,7 +59,7 @@ public abstract partial class PixelConverterTests public static byte[] MakeAbgr32ByteArray(byte r, byte g, byte b, byte a) { - var buffer = new byte[256]; + byte[] buffer = new byte[256]; for (int i = 0; i < buffer.Length; i += 4) { @@ -87,49 +87,18 @@ public abstract partial class PixelConverterTests if (typeof(TSourcePixel) == typeof(TDestinationPixel)) { - Span uniformDest = - MemoryMarshal.Cast(destinationPixels); + Span uniformDest = MemoryMarshal.Cast(destinationPixels); sourcePixels.CopyTo(uniformDest); return; } - // L8 and L16 are special implementations of IPixel in that they do not conform to the - // standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm. - // One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and - // packs/unpacks the pixel without and conversion so we employ custom methods do do this. - if (typeof(TDestinationPixel) == typeof(L16)) - { - ref L16 l16Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationPixels)); - for (int i = 0; i < count; i++) - { - ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); - ref L16 dp = ref Unsafe.Add(ref l16Ref, i); - dp.Pack(sp.ToScaledVector4()); - } - - return; - } - - if (typeof(TDestinationPixel) == typeof(L8)) - { - ref L8 l8Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationPixels)); - for (int i = 0; i < count; i++) - { - ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); - ref L8 dp = ref Unsafe.Add(ref l8Ref, i); - dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); - } - - return; - } - // Normal conversion ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < count; i++) { ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); - dp.FromScaledVector4(sp.ToScaledVector4()); + dp = TDestinationPixel.FromScaledVector4(sp.ToScaledVector4()); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 8c16a72df..b9d9b695a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -39,32 +39,32 @@ public abstract class PixelOperationsTests : MeasureFixture } public static TheoryData ArraySizesData => - new TheoryData - { - 0, - 1, - 2, - 7, - 16, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 1111 - }; + new() + { + 0, + 1, + 2, + 7, + 16, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 1111 + }; protected Configuration Configuration => Configuration.Default; @@ -74,14 +74,14 @@ public abstract class PixelOperationsTests : MeasureFixture internal static TPixel[] CreateExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { - var expected = new TPixel[source.Length]; + TPixel[] expected = new TPixel[source.Length]; for (int i = 0; i < expected.Length; i++) { Vector4 v = source[i]; vectorModifier?.Invoke(ref v); - expected[i].FromVector4(v); + expected[i] = TPixel.FromVector4(v); } return expected; @@ -89,14 +89,14 @@ public abstract class PixelOperationsTests : MeasureFixture internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { - var expected = new TPixel[source.Length]; + TPixel[] expected = new TPixel[source.Length]; for (int i = 0; i < expected.Length; i++) { Vector4 v = source[i]; vectorModifier?.Invoke(ref v); - expected[i].FromScaledVector4(v); + expected[i] = TPixel.FromScaledVector4(v); } return expected; @@ -114,18 +114,16 @@ public abstract class PixelOperationsTests : MeasureFixture { // We use 0 - 255 as we have pixel formats that store // the alpha component in less than 8 bits. - const byte Alpha = byte.MinValue; - const byte NoAlpha = byte.MaxValue; + const byte alpha = byte.MinValue; + const byte noAlpha = byte.MaxValue; - TPixel pixel = default; - pixel.FromRgba32(new Rgba32(0, 0, 0, Alpha)); + TPixel pixel = TPixel.FromRgba32(new Rgba32(0, 0, 0, alpha)); - Rgba32 dest = default; - pixel.ToRgba32(ref dest); + Rgba32 dest = pixel.ToRgba32(); bool hasAlpha = TPixel.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; - byte expectedAlpha = hasAlpha ? Alpha : NoAlpha; + byte expectedAlpha = hasAlpha ? alpha : noAlpha; Assert.Equal(expectedAlpha, dest.A); } @@ -355,7 +353,7 @@ public abstract class PixelOperationsTests : MeasureFixture { const int count = 2134; TPixel[] source = CreatePixelTestData(count); - var expected = new TDestPixel[count]; + TDestPixel[] expected = new TDestPixel[count]; PixelConverterTests.ReferenceImplementations.To(this.Configuration, source, expected); @@ -476,13 +474,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromArgb32Bytes(int count) { byte[] source = CreateByteTestData(count * 4); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i4 = i * 4; - expected[i].FromArgb32(new Argb32(source[i4 + 1], source[i4 + 2], source[i4 + 3], source[i4 + 0])); + expected[i] = TPixel.FromArgb32(new Argb32(source[i4 + 1], source[i4 + 2], source[i4 + 3], source[i4 + 0])); } TestOperation( @@ -497,12 +495,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; - var argb = default(Argb32); for (int i = 0; i < count; i++) { int i4 = i * 4; - argb.FromScaledVector4(source[i].ToScaledVector4()); + Argb32 argb = Argb32.FromScaledVector4(source[i].ToScaledVector4()); expected[i4] = argb.A; expected[i4 + 1] = argb.R; @@ -521,13 +518,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromBgr24Bytes(int count) { byte[] source = CreateByteTestData(count * 3); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i3 = i * 3; - expected[i].FromBgr24(new Bgr24(source[i3 + 2], source[i3 + 1], source[i3])); + expected[i] = TPixel.FromBgr24(new Bgr24(source[i3 + 2], source[i3 + 1], source[i3])); } TestOperation( @@ -542,12 +539,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; - var bgr = default(Bgr24); for (int i = 0; i < count; i++) { int i3 = i * 3; - bgr.FromScaledVector4(source[i].ToScaledVector4()); + Bgr24 bgr = Bgr24.FromScaledVector4(source[i].ToScaledVector4()); expected[i3] = bgr.B; expected[i3 + 1] = bgr.G; expected[i3 + 2] = bgr.R; @@ -564,13 +560,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromBgra32Bytes(int count) { byte[] source = CreateByteTestData(count * 4); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i4 = i * 4; - expected[i].FromBgra32(new Bgra32(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3])); + expected[i] = TPixel.FromBgra32(new Bgra32(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3])); } TestOperation( @@ -585,12 +581,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; - var bgra = default(Bgra32); for (int i = 0; i < count; i++) { int i4 = i * 4; - bgra.FromScaledVector4(source[i].ToScaledVector4()); + Bgra32 bgra = Bgra32.FromScaledVector4(source[i].ToScaledVector4()); expected[i4] = bgra.B; expected[i4 + 1] = bgra.G; expected[i4 + 2] = bgra.R; @@ -608,13 +603,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromAbgr32Bytes(int count) { byte[] source = CreateByteTestData(count * 4); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i4 = i * 4; - expected[i].FromAbgr32(new Abgr32(source[i4 + 3], source[i4 + 2], source[i4 + 1], source[i4 + 0])); + expected[i] = TPixel.FromAbgr32(new Abgr32(source[i4 + 3], source[i4 + 2], source[i4 + 1], source[i4 + 0])); } TestOperation( @@ -629,12 +624,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; - var abgr = default(Abgr32); for (int i = 0; i < count; i++) { int i4 = i * 4; - abgr.FromScaledVector4(source[i].ToScaledVector4()); + Abgr32 abgr = Abgr32.FromScaledVector4(source[i].ToScaledVector4()); expected[i4] = abgr.A; expected[i4 + 1] = abgr.B; expected[i4 + 2] = abgr.G; @@ -653,14 +647,14 @@ public abstract class PixelOperationsTests : MeasureFixture { int size = Unsafe.SizeOf(); byte[] source = CreateByteTestData(count * size); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int offset = i * size; Bgra5551 bgra = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; - expected[i].FromBgra5551(bgra); + expected[i] = TPixel.FromBgra5551(bgra); } TestOperation( @@ -676,12 +670,11 @@ public abstract class PixelOperationsTests : MeasureFixture int size = Unsafe.SizeOf(); TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * size]; - Bgra5551 bgra = default; for (int i = 0; i < count; i++) { int offset = i * size; - bgra.FromScaledVector4(source[i].ToScaledVector4()); + Bgra5551 bgra = Bgra5551.FromScaledVector4(source[i].ToScaledVector4()); OctetBytes bytes = Unsafe.As(ref bgra); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -699,11 +692,11 @@ public abstract class PixelOperationsTests : MeasureFixture { byte[] sourceBytes = CreateByteTestData(count); L8[] source = sourceBytes.Select(b => new L8(b)).ToArray(); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { - expected[i].FromL8(source[i]); + expected[i] = TPixel.FromL8(source[i]); } TestOperation( @@ -717,11 +710,11 @@ public abstract class PixelOperationsTests : MeasureFixture public void ToL8(int count) { TPixel[] source = CreatePixelTestData(count); - var expected = new L8[count]; + L8[] expected = new L8[count]; for (int i = 0; i < count; i++) { - expected[i].FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = L8.FromScaledVector4(source[i].ToScaledVector4()); } TestOperation( @@ -734,18 +727,13 @@ public abstract class PixelOperationsTests : MeasureFixture [MemberData(nameof(ArraySizesData))] public void FromL16(int count) { - L16[] source = CreateVector4TestData(count).Select(v => - { - L16 g = default; - g.FromVector4(v); - return g; - }).ToArray(); + L16[] source = CreateVector4TestData(count).Select(L16.FromVector4).ToArray(); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { - expected[i].FromL16(source[i]); + expected[i] = TPixel.FromL16(source[i]); } TestOperation( @@ -759,11 +747,11 @@ public abstract class PixelOperationsTests : MeasureFixture public void ToL16(int count) { TPixel[] source = CreatePixelTestData(count); - var expected = new L16[count]; + L16[] expected = new L16[count]; for (int i = 0; i < count; i++) { - expected[i].FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = L16.FromScaledVector4(source[i].ToScaledVector4()); } TestOperation( @@ -778,14 +766,14 @@ public abstract class PixelOperationsTests : MeasureFixture { int size = Unsafe.SizeOf(); byte[] source = CreateByteTestData(count * size); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int offset = i * size; La16 la = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; - expected[i].FromLa16(la); + expected[i] = TPixel.FromLa16(la); } TestOperation( @@ -801,12 +789,11 @@ public abstract class PixelOperationsTests : MeasureFixture int size = Unsafe.SizeOf(); TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * size]; - La16 la = default; for (int i = 0; i < count; i++) { int offset = i * size; - la.FromScaledVector4(source[i].ToScaledVector4()); + La16 la = La16.FromScaledVector4(source[i].ToScaledVector4()); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -824,14 +811,14 @@ public abstract class PixelOperationsTests : MeasureFixture { int size = Unsafe.SizeOf(); byte[] source = CreateByteTestData(count * size); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int offset = i * size; La32 la = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; - expected[i].FromLa32(la); + expected[i] = TPixel.FromLa32(la); } TestOperation( @@ -847,12 +834,11 @@ public abstract class PixelOperationsTests : MeasureFixture int size = Unsafe.SizeOf(); TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * size]; - La32 la = default; for (int i = 0; i < count; i++) { int offset = i * size; - la.FromScaledVector4(source[i].ToScaledVector4()); + La32 la = La32.FromScaledVector4(source[i].ToScaledVector4()); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -871,13 +857,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromRgb24Bytes(int count) { byte[] source = CreateByteTestData(count * 3); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i3 = i * 3; - expected[i].FromRgb24(new Rgb24(source[i3 + 0], source[i3 + 1], source[i3 + 2])); + expected[i] = TPixel.FromRgb24(new Rgb24(source[i3 + 0], source[i3 + 1], source[i3 + 2])); } TestOperation( @@ -892,12 +878,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; - var rgb = default(Rgb24); for (int i = 0; i < count; i++) { int i3 = i * 3; - rgb.FromScaledVector4(source[i].ToScaledVector4()); + Rgb24 rgb = Rgb24.FromScaledVector4(source[i].ToScaledVector4()); expected[i3] = rgb.R; expected[i3 + 1] = rgb.G; expected[i3 + 2] = rgb.B; @@ -914,13 +899,13 @@ public abstract class PixelOperationsTests : MeasureFixture public void FromRgba32Bytes(int count) { byte[] source = CreateByteTestData(count * 4); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i4 = i * 4; - expected[i].FromRgba32(new Rgba32(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3])); + expected[i] = TPixel.FromRgba32(new Rgba32(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3])); } TestOperation( @@ -935,12 +920,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; - var rgba = default(Rgba32); for (int i = 0; i < count; i++) { int i4 = i * 4; - rgba.FromScaledVector4(source[i].ToScaledVector4()); + Rgba32 rgba = Rgba32.FromScaledVector4(source[i].ToScaledVector4()); expected[i4] = rgba.R; expected[i4 + 1] = rgba.G; expected[i4 + 2] = rgba.B; @@ -959,12 +943,12 @@ public abstract class PixelOperationsTests : MeasureFixture { byte[] source = CreateByteTestData(count * 6); Span sourceSpan = source.AsSpan(); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i6 = i * 6; - expected[i].FromRgb48(MemoryMarshal.Cast(sourceSpan.Slice(i6, 6))[0]); + expected[i] = TPixel.FromRgb48(MemoryMarshal.Cast(sourceSpan.Slice(i6, 6))[0]); } TestOperation( @@ -979,12 +963,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 6]; - Rgb48 rgb = default; for (int i = 0; i < count; i++) { int i6 = i * 6; - rgb.FromScaledVector4(source[i].ToScaledVector4()); + Rgb48 rgb = Rgb48.FromScaledVector4(source[i].ToScaledVector4()); OctetBytes rgb48Bytes = Unsafe.As(ref rgb); expected[i6] = rgb48Bytes[0]; expected[i6 + 1] = rgb48Bytes[1]; @@ -1006,12 +989,12 @@ public abstract class PixelOperationsTests : MeasureFixture { byte[] source = CreateByteTestData(count * 8); Span sourceSpan = source.AsSpan(); - var expected = new TPixel[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { int i8 = i * 8; - expected[i].FromRgba64(MemoryMarshal.Cast(sourceSpan.Slice(i8, 8))[0]); + expected[i] = TPixel.FromRgba64(MemoryMarshal.Cast(sourceSpan.Slice(i8, 8))[0]); } TestOperation( @@ -1026,12 +1009,11 @@ public abstract class PixelOperationsTests : MeasureFixture { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 8]; - Rgba64 rgba = default; for (int i = 0; i < count; i++) { int i8 = i * 8; - rgba.FromScaledVector4(source[i].ToScaledVector4()); + Rgba64 rgba = Rgba64.FromScaledVector4(source[i].ToScaledVector4()); OctetBytes rgba64Bytes = Unsafe.As(ref rgba); expected[i8] = rgba64Bytes[0]; expected[i8 + 1] = rgba64Bytes[1]; @@ -1060,11 +1042,11 @@ public abstract class PixelOperationsTests : MeasureFixture internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction vectorModifier = null) { - var expected = new Vector4[source.Length]; + Vector4[] expected = new Vector4[source.Length]; for (int i = 0; i < expected.Length; i++) { - var v = source[i].ToVector4(); + Vector4 v = source[i].ToVector4(); vectorModifier?.Invoke(ref v); @@ -1076,7 +1058,7 @@ public abstract class PixelOperationsTests : MeasureFixture internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source, RefAction vectorModifier = null) { - var expected = new Vector4[source.Length]; + Vector4[] expected = new Vector4[source.Length]; for (int i = 0; i < expected.Length; i++) { @@ -1098,7 +1080,7 @@ public abstract class PixelOperationsTests : MeasureFixture where TSource : struct where TDest : struct { - using (var buffers = new TestBuffers(source, expected, preferExactComparison)) + using (TestBuffers buffers = new(source, expected, preferExactComparison)) { action(buffers.SourceBuffer, buffers.ActualDestBuffer); buffers.Verify(); @@ -1107,8 +1089,8 @@ public abstract class PixelOperationsTests : MeasureFixture internal static Vector4[] CreateVector4TestData(int length, RefAction vectorModifier = null) { - var result = new Vector4[length]; - var rnd = new Random(42); // Deterministic random values + Vector4[] result = new Vector4[length]; + Random rnd = new(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -1123,9 +1105,9 @@ public abstract class PixelOperationsTests : MeasureFixture internal static TPixel[] CreatePixelTestData(int length, RefAction vectorModifier = null) { - var result = new TPixel[length]; + TPixel[] result = new TPixel[length]; - var rnd = new Random(42); // Deterministic random values + Random rnd = new(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -1133,7 +1115,7 @@ public abstract class PixelOperationsTests : MeasureFixture vectorModifier?.Invoke(ref v); - result[i].FromVector4(v); + result[i] = TPixel.FromVector4(v); } return result; @@ -1141,9 +1123,9 @@ public abstract class PixelOperationsTests : MeasureFixture internal static TPixel[] CreateScaledPixelTestData(int length, RefAction vectorModifier = null) { - var result = new TPixel[length]; + TPixel[] result = new TPixel[length]; - var rnd = new Random(42); // Deterministic random values + Random rnd = new(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -1151,7 +1133,7 @@ public abstract class PixelOperationsTests : MeasureFixture vectorModifier?.Invoke(ref v); - result[i].FromScaledVector4(v); + result[i] = TPixel.FromScaledVector4(v); } return result; @@ -1160,7 +1142,7 @@ public abstract class PixelOperationsTests : MeasureFixture internal static byte[] CreateByteTestData(int length, int seed = 42) { byte[] result = new byte[length]; - var rnd = new Random(seed); // Deterministic random values + Random rnd = new(seed); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -1171,7 +1153,7 @@ public abstract class PixelOperationsTests : MeasureFixture } internal static Vector4 GetScaledVector(Random rnd) - => new Vector4((float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble()); + => new((float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble()); [StructLayout(LayoutKind.Sequential)] internal unsafe struct OctetBytes @@ -1219,7 +1201,7 @@ public abstract class PixelOperationsTests : MeasureFixture { Span expected = MemoryMarshal.Cast(this.ExpectedDestBuffer.AsSpan()); Span actual = MemoryMarshal.Cast(this.ActualDestBuffer.GetSpan()); - var comparer = new ApproximateFloatComparer(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); + ApproximateFloatComparer comparer = new(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); for (int i = 0; i < count; i++) { @@ -1230,7 +1212,7 @@ public abstract class PixelOperationsTests : MeasureFixture { Span expected = this.ExpectedDestBuffer.AsSpan(); Span actual = this.ActualDestBuffer.GetSpan(); - var comparer = new ApproximateFloatComparer(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); + ApproximateFloatComparer comparer = new(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); for (int i = 0; i < count; i++) { diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index fac767812..b2790469a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -13,8 +13,8 @@ public class Rg32Tests [Fact] public void Rg32_PackedValues() { - float x = 0xb6dc; - float y = 0xA59f; + const float x = 0xb6dc; + const float y = 0xA59f; Assert.Equal(0xa59fb6dc, new Rg32(x / 0xffff, y / 0xffff).PackedValue); Assert.Equal(6554U, new Rg32(0.1f, -0.3f).PackedValue); @@ -51,12 +51,11 @@ public class Rg32Tests { // arrange Rg32 rg32 = new(Vector2.One); - Rg32 pixel = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rg32.ToScaledVector4(); - pixel.FromScaledVector4(scaled); + Rg32 pixel = Rg32.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -67,11 +66,10 @@ public class Rg32Tests public void Rg32_FromBgra5551() { // arrange - Rg32 rg32 = new(Vector2.One); - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - rg32.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rg32 rg32 = Rg32.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, rg32.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index 385126a65..6364378c1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -68,8 +68,7 @@ public class Rgb24Tests [Fact] public void FromRgba32() { - Rgb24 rgb = default; - rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); + Rgb24 rgb = Rgb24.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -85,8 +84,7 @@ public class Rgb24Tests [Fact] public void FromVector4() { - Rgb24 rgb = default; - rgb.FromVector4(Vec(1, 2, 3, 4)); + Rgb24 rgb = Rgb24.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -106,11 +104,10 @@ public class Rgb24Tests { // arrange Rgb24 rgb = new(1, 2, 3); - Rgba32 rgba = default; Rgba32 expected = new(1, 2, 3, 255); // act - rgb.ToRgba32(ref rgba); + Rgba32 rgba = rgb.ToRgba32(); // assert Assert.Equal(expected, rgba); @@ -119,11 +116,8 @@ public class Rgb24Tests [Fact] public void Rgb24_FromBgra5551() { - // arrange - Rgb24 rgb = new(255, 255, 255); - // act - rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rgb24 rgb = Rgb24.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(255, rgb.R); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index 625a9187f..764627ee3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -33,13 +33,12 @@ public class Rgb48Tests public void Rgb48_FromScaledVector4() { // arrange - Rgb48 pixel = default; Rgb48 short3 = new(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); Rgb48 expected = new(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); // act Vector4 scaled = short3.ToScaledVector4(); - pixel.FromScaledVector4(scaled); + Rgb48 pixel = Rgb48.FromScaledVector4(scaled); // assert Assert.Equal(expected, pixel); @@ -53,8 +52,7 @@ public class Rgb48Tests Rgba32 expected = new(20, 38, 76, 255); // act - Rgba32 actual = default; - rgba48.ToRgba32(ref actual); + Rgba32 actual = rgba48.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -64,11 +62,10 @@ public class Rgb48Tests public void Rgb48_FromBgra5551() { // arrange - Rgb48 rgb = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rgb48 rgb = Rgb48.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, rgb.R); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index a8243064d..79a1aefc9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -18,8 +18,8 @@ public class Rgba1010102Tests { Rgba1010102 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); Rgba1010102 color2 = new(new Vector4(0.0f)); - Rgba1010102 color3 = new(new Vector4(1.0f, 0.0f, 1.0f, 1.0f)); - Rgba1010102 color4 = new(1.0f, 0.0f, 1.0f, 1.0f); + Rgba1010102 color3 = new(new Vector4(1f, 0.0f, 1f, 1f)); + Rgba1010102 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -32,9 +32,9 @@ public class Rgba1010102Tests public void AreNotEqual() { Rgba1010102 color1 = new(0.0f, 0.0f, 0.0f, 0.0f); - Rgba1010102 color2 = new(new Vector4(1.0f)); - Rgba1010102 color3 = new(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); - Rgba1010102 color4 = new(1.0f, 1.0f, 0.0f, 1.0f); + Rgba1010102 color2 = new(new Vector4(1f)); + Rgba1010102 color3 = new(new Vector4(1f, 0.0f, 0.0f, 1f)); + Rgba1010102 color4 = new(1f, 1f, 0.0f, 1f); Assert.NotEqual(color1, color2); Assert.NotEqual(color3, color4); @@ -43,10 +43,10 @@ public class Rgba1010102Tests [Fact] public void Rgba1010102_PackedValue() { - float x = 0x2db; - float y = 0x36d; - float z = 0x3b7; - float w = 0x1; + const float x = 0x2db; + const float y = 0x36d; + const float z = 0x3b7; + const float w = 0x1; Assert.Equal(0x7B7DB6DBU, new Rgba1010102(x / 0x3ff, y / 0x3ff, z / 0x3ff, w / 3).PackedValue); Assert.Equal(536871014U, new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f).PackedValue); @@ -84,12 +84,11 @@ public class Rgba1010102Tests { // arrange Rgba1010102 rgba = new(Vector4.One); - Rgba1010102 actual = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rgba.ToScaledVector4(); - actual.FromScaledVector4(scaled); + Rgba1010102 actual = Rgba1010102.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -99,11 +98,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromBgra5551() { // arrange - Rgba1010102 rgba = new(Vector4.One); - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - rgba.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rgba1010102 rgba = Rgba1010102.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, rgba.PackedValue); @@ -113,11 +111,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromArgb32() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromArgb32(new Argb32(255, 255, 255, 255)); + Rgba1010102 rgba = Rgba1010102.FromArgb32(new Argb32(255, 255, 255, 255)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -127,14 +124,12 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgba32() { // arrange - Rgba1010102 rgba1 = default; - Rgba1010102 rgba2 = default; - uint expectedPackedValue1 = uint.MaxValue; - uint expectedPackedValue2 = 0xFFF003FF; + const uint expectedPackedValue1 = uint.MaxValue; + const uint expectedPackedValue2 = 0xFFF003FF; // act - rgba1.FromRgba32(new Rgba32(255, 255, 255, 255)); - rgba2.FromRgba32(new Rgba32(255, 0, 255, 255)); + Rgba1010102 rgba1 = Rgba1010102.FromRgba32(new Rgba32(255, 255, 255, 255)); + Rgba1010102 rgba2 = Rgba1010102.FromRgba32(new Rgba32(255, 0, 255, 255)); // assert Assert.Equal(expectedPackedValue1, rgba1.PackedValue); @@ -145,11 +140,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromBgr24() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromBgr24(new Bgr24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -159,11 +153,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromGrey8() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromL8(new L8(byte.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -173,11 +166,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromGrey16() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromL16(new L16(ushort.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -187,11 +179,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgb24() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromRgb24(new Rgb24(byte.MaxValue, byte.MaxValue, byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -201,11 +192,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgb48() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromRgb48(new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -215,11 +205,10 @@ public class Rgba1010102Tests public void Rgba1010102_FromRgba64() { // arrange - Rgba1010102 rgba = default; - uint expectedPackedValue = uint.MaxValue; + const uint expectedPackedValue = uint.MaxValue; // act - rgba.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); + Rgba1010102 rgba = Rgba1010102.FromRgba64(new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -240,8 +229,7 @@ public class Rgba1010102Tests Rgba32 expected = new(25, 0, 128, 0); // act - Rgba32 actual = default; - rgba.ToRgba32(ref actual); + Rgba32 actual = rgba.ToRgba32(); // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 35fab151c..6d56185ec 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -164,12 +164,11 @@ public class Rgba32Tests { // arrange Rgba32 rgba = new(Vector4.One); - Rgba32 actual = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rgba.ToScaledVector4(); - actual.FromScaledVector4(scaled); + Rgba32 actual = Rgba32.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -187,11 +186,10 @@ public class Rgba32Tests { // arrange Rgba32 rgba = new(+0.1f, -0.3f, +0.5f, -0.7f); - Rgba32 actual = default; Rgba32 expected = new(0x1a, 0, 0x80, 0); // act - actual.FromRgba32(rgba); + Rgba32 actual = Rgba32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -201,13 +199,11 @@ public class Rgba32Tests public void Rgba32_FromRgba32_ToRgba32() { // arrange - Rgba32 rgba = default; - Rgba32 actual = default; Rgba32 expected = new(0x1a, 0, 0x80, 0); // act - rgba.FromRgba32(expected); - actual.FromRgba32(rgba); + Rgba32 rgba = Rgba32.FromRgba32(expected); + Rgba32 actual = Rgba32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -217,13 +213,11 @@ public class Rgba32Tests public void Rgba32_FromBgra32_ToRgba32() { // arrange - Rgba32 rgba = default; - Bgra32 actual = default; Bgra32 expected = new(0x1a, 0, 0x80, 0); // act - rgba.FromBgra32(expected); - actual.FromRgba32(rgba); + Rgba32 rgba = Rgba32.FromBgra32(expected); + Bgra32 actual = Bgra32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -233,13 +227,11 @@ public class Rgba32Tests public void Rgba32_FromAbgr32_ToRgba32() { // arrange - Rgba32 rgba = default; - Abgr32 actual = default; Abgr32 expected = new(0x1a, 0, 0x80, 0); // act - rgba.FromAbgr32(expected); - actual.FromRgba32(rgba); + Rgba32 rgba = Rgba32.FromAbgr32(expected); + Abgr32 actual = Abgr32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -249,13 +241,11 @@ public class Rgba32Tests public void Rgba32_FromArgb32_ToArgb32() { // arrange - Rgba32 rgba = default; - Argb32 actual = default; Argb32 expected = new(0x1a, 0, 0x80, 0); // act - rgba.FromArgb32(expected); - actual.FromRgba32(rgba); + Rgba32 rgba = Rgba32.FromArgb32(expected); + Argb32 actual = Argb32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -265,13 +255,11 @@ public class Rgba32Tests public void Rgba32_FromRgb48() { // arrange - Rgba32 input = default; - Rgb48 actual = default; Rgb48 expected = new(65535, 0, 65535); // act - input.FromRgb48(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Rgba32 input = Rgba32.FromRgb48(expected); + Rgb48 actual = Rgb48.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -281,13 +269,11 @@ public class Rgba32Tests public void Rgba32_FromRgba64() { // arrange - Rgba32 input = default; - Rgba64 actual = default; Rgba64 expected = new(65535, 0, 65535, 0); // act - input.FromRgba64(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Rgba32 input = Rgba32.FromRgba64(expected); + Rgba64 actual = Rgba64.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -297,11 +283,10 @@ public class Rgba32Tests public void Rgba32_FromBgra5551() { // arrange - Rgba32 rgb = default; - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rgba32 rgb = Rgba32.FromBgra5551(new Bgra5551(1f, 1f, 1f, 1f)); // assert Assert.Equal(expected, rgb.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 4f335b1e8..694d0ace1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -69,8 +69,7 @@ public class Rgba64Tests // act Vector4 scaled = source.ToScaledVector4(); - Rgba64 actual = default; - actual.FromScaledVector4(scaled); + Rgba64 actual = Rgba64.FromScaledVector4(scaled); // assert Assert.Equal(source, actual); @@ -79,10 +78,9 @@ public class Rgba64Tests [Fact] public void Rgba64_Clamping() { - Rgba64 zero = default; - Rgba64 one = default; - zero.FromVector4(Vector4.One * -1234.0f); - one.FromVector4(Vector4.One * 1234.0f); + Rgba64 zero = Rgba64.FromVector4(Vector4.One * -1234.0f); + Rgba64 one = Rgba64.FromVector4(Vector4.One * 1234.0f); + Assert.Equal(Vector4.Zero, zero.ToVector4()); Assert.Equal(Vector4.One, one.ToVector4()); } @@ -92,11 +90,10 @@ public class Rgba64Tests { // arrange Rgba64 rgba64 = new(5140, 9766, 19532, 29555); - Rgba32 actual = default; Rgba32 expected = new(20, 38, 76, 115); // act - rgba64.ToRgba32(ref actual); + Rgba32 actual = rgba64.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -106,11 +103,10 @@ public class Rgba64Tests public void Rgba64_FromBgra5551() { // arrange - Rgba64 rgba = default; - ushort expected = ushort.MaxValue; + const ushort expected = ushort.MaxValue; // act - rgba.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Rgba64 rgba = Rgba64.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, rgba.R); @@ -147,8 +143,7 @@ public class Rgba64Tests Rgba32 source = new(20, 38, 76, 115); Rgba64 expected = new(5140, 9766, 19532, 29555); - Rgba64 actual = default; - actual.FromRgba32(source); + Rgba64 actual = Rgba64.FromRgba32(source); Assert.Equal(expected, actual); } @@ -217,8 +212,7 @@ public class Rgba64Tests public void ConstructFrom_Vector4() { Vector4 source = new(0f, 0.2f, 0.5f, 1f); - Rgba64 expected = default; - expected.FromScaledVector4(source); + Rgba64 expected = Rgba64.FromScaledVector4(source); Rgba64 actual = new(source); diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index b263bf8ea..5273482ef 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -119,13 +119,11 @@ public class RgbaVectorTests public void RgbaVector_FromRgb48() { // arrange - RgbaVector input = default; - Rgb48 actual = default; Rgb48 expected = new(65535, 0, 65535); // act - input.FromRgb48(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + RgbaVector input = RgbaVector.FromRgb48(expected); + Rgb48 actual = Rgb48.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -135,13 +133,11 @@ public class RgbaVectorTests public void RgbaVector_FromRgba64() { // arrange - RgbaVector input = default; - Rgba64 actual = default; Rgba64 expected = new(65535, 0, 65535, 0); // act - input.FromRgba64(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + RgbaVector input = RgbaVector.FromRgba64(expected); + Rgba64 actual = Rgba64.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -151,11 +147,10 @@ public class RgbaVectorTests public void RgbaVector_FromBgra5551() { // arrange - RgbaVector rgb = default; Vector4 expected = Vector4.One; // act - rgb.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + RgbaVector rgb = RgbaVector.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, rgb.ToScaledVector4()); @@ -165,11 +160,10 @@ public class RgbaVectorTests public void RgbaVector_FromGrey16() { // arrange - RgbaVector rgba = default; Vector4 expected = Vector4.One; // act - rgba.FromL16(new L16(ushort.MaxValue)); + RgbaVector rgba = RgbaVector.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, rgba.ToScaledVector4()); @@ -179,11 +173,10 @@ public class RgbaVectorTests public void RgbaVector_FromGrey8() { // arrange - RgbaVector rgba = default; Vector4 expected = Vector4.One; // act - rgba.FromL8(new L8(byte.MaxValue)); + RgbaVector rgba = RgbaVector.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, rgba.ToScaledVector4()); @@ -197,10 +190,8 @@ public class RgbaVectorTests using Image source = new(Configuration.Default, 1, 1, green); using Image clone = source.CloneAs(); - Rgba32 srcColor = default; - Rgba32 cloneColor = default; - source[0, 0].ToRgba32(ref srcColor); - clone[0, 0].ToRgba32(ref cloneColor); + Rgba32 srcColor = source[0, 0].ToRgba32(); + Rgba32 cloneColor = clone[0, 0].ToRgba32(); Assert.Equal(srcColor, cloneColor); } diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index 1143925cb..f23da0c7a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -68,13 +68,12 @@ public class Short2Tests public void Short2_FromScaledVector4() { // arrange - Short2 pixel = default; Short2 short2 = new(Vector2.One * 0x7FFF); const ulong expected = 0x7FFF7FFF; // act Vector4 scaled = short2.ToScaledVector4(); - pixel.FromScaledVector4(scaled); + Short2 pixel = Short2.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -86,11 +85,10 @@ public class Short2Tests { // arrange Short2 short2 = new(127.5f, -5.3f); - Rgba32 actual = default; Rgba32 expected = new(128, 127, 0, 255); // act - short2.ToRgba32(ref actual); + Rgba32 actual = short2.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -100,13 +98,11 @@ public class Short2Tests public void Short2_FromRgba32_ToRgba32() { // arrange - Short2 short2 = default; - Rgba32 actual = default; Rgba32 expected = new(20, 38, 0, 255); // act - short2.FromRgba32(expected); - short2.ToRgba32(ref actual); + Short2 short2 = Short2.FromRgba32(expected); + Rgba32 actual = short2.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -116,13 +112,11 @@ public class Short2Tests public void Short2_FromRgb48() { // arrange - Short2 input = default; - Rgb48 actual = default; Rgb48 expected = new(65535, 65535, 0); // act - input.FromRgb48(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Short2 input = Short2.FromRgb48(expected); + Rgb48 actual = Rgb48.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -132,13 +126,11 @@ public class Short2Tests public void Short2_FromRgba64() { // arrange - Short2 input = default; - Rgba64 actual = default; Rgba64 expected = new(65535, 65535, 0, 65535); // act - input.FromRgba64(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Short2 input = Short2.FromRgba64(expected); + Rgba64 actual = Rgba64.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -147,11 +139,8 @@ public class Short2Tests [Fact] public void Short2_FromBgra5551() { - // arrange - Short2 short2 = default; - // act - short2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Short2 short2 = Short2.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Vector4 actual = short2.ToScaledVector4(); diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index ab3cc3b6e..819ff0e1e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -60,8 +60,7 @@ public class Short4Tests const long expected = 0x7FFF7FFF7FFF7FFF; // act - Short4 pixel = default; - pixel.FromScaledVector4(scaled); + Short4 pixel = Short4.FromScaledVector4(scaled); // assert Assert.Equal((ulong)expected, pixel.PackedValue); @@ -88,11 +87,10 @@ public class Short4Tests { // arrange Short4 shortValue = new(11547, 12653, 29623, 193); - Rgba32 actual = default; Rgba32 expected = new(172, 177, 243, 128); // act - shortValue.ToRgba32(ref actual); + Rgba32 actual = shortValue.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -102,13 +100,11 @@ public class Short4Tests public void Short4_FromRgba32_ToRgba32() { // arrange - Short4 short4 = default; - Rgba32 actual = default; Rgba32 expected = new(20, 38, 0, 255); // act - short4.FromRgba32(expected); - short4.ToRgba32(ref actual); + Short4 short4 = Short4.FromRgba32(expected); + Rgba32 actual = short4.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -118,15 +114,12 @@ public class Short4Tests public void Short4_FromBgra32_ToRgba32() { // arrange - Short4 short4 = default; - Bgra32 actual = default; Bgra32 expected = new(20, 38, 0, 255); // act - short4.FromBgra32(expected); - Rgba32 temp = default; - short4.ToRgba32(ref temp); - actual.FromRgba32(temp); + Short4 short4 = Short4.FromBgra32(expected); + Rgba32 temp = short4.ToRgba32(); + Bgra32 actual = Bgra32.FromRgba32(temp); // assert Assert.Equal(expected, actual); @@ -136,15 +129,12 @@ public class Short4Tests public void Short4_FromArgb32_ToRgba32() { // arrange - Short4 short4 = default; - Argb32 actual = default; Argb32 expected = new(20, 38, 0, 255); // act - short4.FromArgb32(expected); - Rgba32 temp = default; - short4.ToRgba32(ref temp); - actual.FromRgba32(temp); + Short4 short4 = Short4.FromArgb32(expected); + Rgba32 temp = short4.ToRgba32(); + Argb32 actual = Argb32.FromRgba32(temp); // assert Assert.Equal(expected, actual); @@ -154,15 +144,12 @@ public class Short4Tests public void Short4_FromAbgrb32_ToRgba32() { // arrange - Short4 short4 = default; - Abgr32 actual = default; Abgr32 expected = new(20, 38, 0, 255); // act - short4.FromAbgr32(expected); - Rgba32 temp = default; - short4.ToRgba32(ref temp); - actual.FromRgba32(temp); + Short4 short4 = Short4.FromAbgr32(expected); + Rgba32 temp = short4.ToRgba32(); + Abgr32 actual = Abgr32.FromRgba32(temp); // assert Assert.Equal(expected, actual); @@ -172,13 +159,11 @@ public class Short4Tests public void Short4_FromRgb48_ToRgb48() { // arrange - Short4 input = default; - Rgb48 actual = default; Rgb48 expected = new(65535, 0, 65535); // act - input.FromRgb48(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Short4 input = Short4.FromRgb48(expected); + Rgb48 actual = Rgb48.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -188,13 +173,11 @@ public class Short4Tests public void Short4_FromRgba64_ToRgba64() { // arrange - Short4 input = default; - Rgba64 actual = default; Rgba64 expected = new(65535, 0, 65535, 0); // act - input.FromRgba64(expected); - actual.FromScaledVector4(input.ToScaledVector4()); + Short4 input = Short4.FromRgba64(expected); + Rgba64 actual = Rgba64.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); @@ -204,11 +187,10 @@ public class Short4Tests public void Short4_FromBgra5551() { // arrange - Short4 short4 = default; Vector4 expected = Vector4.One; // act - short4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + Short4 short4 = Short4.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); // assert Assert.Equal(expected, short4.ToScaledVector4()); diff --git a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 75e3b693c..651f6fe7f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs @@ -84,10 +84,8 @@ public class UnPackedPixelTests Rgba32 color = new(24, 48, 96, 192); RgbaVector colorVector = new(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); - Rgba32 rgba = default; - Rgba32 rgbaVector = default; - color.ToRgba32(ref rgba); - colorVector.ToRgba32(ref rgbaVector); + Rgba32 rgba = color.ToRgba32(); + Rgba32 rgbaVector = colorVector.ToRgba32(); Assert.Equal(rgba, rgbaVector); } diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs index cd61d68be..81ba1693d 100644 --- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs +++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs @@ -21,14 +21,7 @@ public class IntegralImageTests : BaseImageOperationsExtensionTest Buffer2D integralBuffer = image.CalculateIntegralImage(); // Assert: - VerifySumValues(provider, integralBuffer, (Rgba32 pixel) => - { - L8 outputPixel = default; - - outputPixel.FromRgba32(pixel); - - return outputPixel.PackedValue; - }); + VerifySumValues(provider, integralBuffer, (Rgba32 pixel) => L8.FromRgba32(pixel).PackedValue); } [Theory] @@ -45,14 +38,7 @@ public class IntegralImageTests : BaseImageOperationsExtensionTest Buffer2D integralBuffer = image.CalculateIntegralImage(interest); // Assert: - VerifySumValues(provider, integralBuffer, interest, (Rgba32 pixel) => - { - L8 outputPixel = default; - - outputPixel.FromRgba32(pixel); - - return outputPixel.PackedValue; - }); + VerifySumValues(provider, integralBuffer, interest, (Rgba32 pixel) => L8.FromRgba32(pixel).PackedValue); } [Theory] @@ -88,7 +74,7 @@ public class IntegralImageTests : BaseImageOperationsExtensionTest private static void VerifySumValues( TestImageProvider provider, Buffer2D integralBuffer, - System.Func getPixel) + Func getPixel) where TPixel : unmanaged, IPixel => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds(), getPixel); @@ -96,7 +82,7 @@ public class IntegralImageTests : BaseImageOperationsExtensionTest TestImageProvider provider, Buffer2D integralBuffer, Rectangle bounds, - System.Func getPixel) + Func getPixel) where TPixel : unmanaged, IPixel { Buffer2DRegion image = provider.GetImage().GetRootFramePixelBuffer().GetRegion(bounds); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 0fc5e286d..2ed003fdd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -21,8 +21,8 @@ public class AffineTransformTests /// angleDeg, sx, sy, tx, ty /// public static readonly TheoryData TransformValues - = new TheoryData - { + = new() + { { 0, 1, 1, 0, 0 }, { 50, 1, 1, 0, 0 }, { 0, 1, 1, 20, 10 }, @@ -35,7 +35,7 @@ public class AffineTransformTests { 0, 1f, 2f, 0, 0 }, }; - public static readonly TheoryData ResamplerNames = new TheoryData + public static readonly TheoryData ResamplerNames = new() { nameof(KnownResamplers.Bicubic), nameof(KnownResamplers.Box), @@ -55,8 +55,8 @@ public class AffineTransformTests }; public static readonly TheoryData Transform_DoesNotCreateEdgeArtifacts_ResamplerNames = - new TheoryData - { + new() + { nameof(KnownResamplers.NearestNeighbor), nameof(KnownResamplers.Triangle), nameof(KnownResamplers.Bicubic), @@ -75,16 +75,14 @@ public class AffineTransformTests where TPixel : unmanaged, IPixel { IResampler resampler = GetResampler(resamplerName); - using (Image image = provider.GetImage()) - { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(30); + using Image image = provider.GetImage(); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(30); - image.Mutate(c => c.Transform(builder, resampler)); - image.DebugSave(provider, resamplerName); + image.Mutate(c => c.Transform(builder, resampler)); + image.DebugSave(provider, resamplerName); - VerifyAllPixelsAreWhiteOrTransparent(image); - } + VerifyAllPixelsAreWhiteOrTransparent(image); } [Theory] @@ -98,22 +96,20 @@ public class AffineTransformTests float ty) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.DebugSave(provider, $"_original"); - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(angleDeg) - .AppendScale(new SizeF(sx, sy)) - .AppendTranslation(new PointF(tx, ty)); + using Image image = provider.GetImage(); + image.DebugSave(provider, $"_original"); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(angleDeg) + .AppendScale(new SizeF(sx, sy)) + .AppendTranslation(new PointF(tx, ty)); - this.PrintMatrix(builder.BuildMatrix(image.Size)); + this.PrintMatrix(builder.BuildMatrix(image.Size)); - image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); + image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); - FormattableString testOutputDetails = $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})"; - image.DebugSave(provider, testOutputDetails); - image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails); - } + FormattableString testOutputDetails = $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})"; + image.DebugSave(provider, testOutputDetails); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails); } [Theory] @@ -121,23 +117,21 @@ public class AffineTransformTests public void Transform_RotateScale_ManuallyCentered(TestImageProvider provider, float angleDeg, float s) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(angleDeg) - .AppendScale(new SizeF(s, s)); + using Image image = provider.GetImage(); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(angleDeg) + .AppendScale(new SizeF(s, s)); - image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); + image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); - FormattableString testOutputDetails = $"R({angleDeg})_S({s})"; - image.DebugSave(provider, testOutputDetails); - image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails); - } + FormattableString testOutputDetails = $"R({angleDeg})_S({s})"; + image.DebugSave(provider, testOutputDetails); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails); } public static readonly TheoryData Transform_IntoRectangle_Data = - new TheoryData - { + new() + { { 0, 0, 10, 10 }, { 0, 0, 5, 10 }, { 0, 0, 10, 5 }, @@ -155,19 +149,17 @@ public class AffineTransformTests public void Transform_FromSourceRectangle1(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var rectangle = new Rectangle(48, 0, 48, 24); + Rectangle rectangle = new(48, 0, 48, 24); - using (Image image = provider.GetImage()) - { - image.DebugSave(provider, $"_original"); - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendScale(new SizeF(2, 1.5F)); + using Image image = provider.GetImage(); + image.DebugSave(provider, $"_original"); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendScale(new SizeF(2, 1.5F)); - image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); + image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); - } + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); } [Theory] @@ -175,18 +167,16 @@ public class AffineTransformTests public void Transform_FromSourceRectangle2(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var rectangle = new Rectangle(0, 24, 48, 24); + Rectangle rectangle = new(0, 24, 48, 24); - using (Image image = provider.GetImage()) - { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendScale(new SizeF(1F, 2F)); + using Image image = provider.GetImage(); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendScale(new SizeF(1F, 2F)); - image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); + image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); - } + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); } [Theory] @@ -195,17 +185,15 @@ public class AffineTransformTests where TPixel : unmanaged, IPixel { IResampler sampler = GetResampler(resamplerName); - using (Image image = provider.GetImage()) - { - AffineTransformBuilder builder = new AffineTransformBuilder() - .AppendRotationDegrees(50) - .AppendScale(new SizeF(.6F, .6F)); + using Image image = provider.GetImage(); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(50) + .AppendScale(new SizeF(.6F, .6F)); - image.Mutate(i => i.Transform(builder, sampler)); + image.Mutate(i => i.Transform(builder, sampler)); - image.DebugSave(provider, resamplerName); - image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); - } + image.DebugSave(provider, resamplerName); + image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); } [Theory] @@ -224,7 +212,7 @@ public class AffineTransformTests [Fact] public void Issue1911() { - using var image = new Image(100, 100); + using Image image = new(100, 100); image.Mutate(x => x = x.Transform(new Rectangle(0, 0, 99, 100), Matrix3x2.Identity, new Size(99, 100), KnownResamplers.Lanczos2)); Assert.Equal(99, image.Width); @@ -254,7 +242,7 @@ public class AffineTransformTests { using Image image = provider.GetImage(); - var m = Matrix3x2.CreateRotation(radians, new Vector2(50, 50)); + Matrix3x2 m = Matrix3x2.CreateRotation(radians, new Vector2(50, 50)); Rectangle r = new(25, 25, 50, 50); image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); image.DebugSave(provider, testOutputDetails: radians); @@ -263,12 +251,8 @@ public class AffineTransformTests private static IResampler GetResampler(string name) { - PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); - - if (property is null) - { - throw new Exception($"No resampler named {name}"); - } + PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name) + ?? throw new InvalidOperationException($"No resampler named {name}"); return (IResampler)property.GetValue(null); } @@ -277,11 +261,10 @@ public class AffineTransformTests where TPixel : unmanaged, IPixel { Assert.True(image.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out Memory data)); - var white = new Rgb24(255, 255, 255); + Rgb24 white = new(255, 255, 255); foreach (TPixel pixel in data.Span) { - Rgba32 rgba = default; - pixel.ToRgba32(ref rgba); + Rgba32 rgba = pixel.ToRgba32(); if (rgba.A == 0) { continue; diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 983cf4d46..89e2a11a9 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -263,6 +263,12 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public struct TestPixelForAgnosticDecode : IPixel { + public readonly Rgba32 ToRgba32() => default; + + public readonly Vector4 ToScaledVector4() => default; + + public readonly Vector4 ToVector4() => default; + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( PixelComponentInfo.Create(2, 8, 8), @@ -271,73 +277,35 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat public PixelOperations CreatePixelOperations() => new(); - public void FromScaledVector4(Vector4 vector) - { - } + public static TestPixelForAgnosticDecode FromScaledVector4(Vector4 vector) => default; - public Vector4 ToScaledVector4() => default; + public static TestPixelForAgnosticDecode FromVector4(Vector4 vector) => default; - public void FromVector4(Vector4 vector) - { - } + public static TestPixelForAgnosticDecode FromAbgr32(Abgr32 source) => default; - public Vector4 ToVector4() => default; + public static TestPixelForAgnosticDecode FromArgb32(Argb32 source) => default; - public void FromArgb32(Argb32 source) - { - } + public static TestPixelForAgnosticDecode FromBgra5551(Bgra5551 source) => default; - public void FromBgra5551(Bgra5551 source) - { - } - - public void FromBgr24(Bgr24 source) - { - } - - public void FromBgra32(Bgra32 source) - { - } - - public void FromAbgr32(Abgr32 source) - { - } + public static TestPixelForAgnosticDecode FromBgr24(Bgr24 source) => default; - public void FromL8(L8 source) - { - } + public static TestPixelForAgnosticDecode FromBgra32(Bgra32 source) => default; - public void FromL16(L16 source) - { - } + public static TestPixelForAgnosticDecode FromL8(L8 source) => default; - public void FromLa16(La16 source) - { - } + public static TestPixelForAgnosticDecode FromL16(L16 source) => default; - public void FromLa32(La32 source) - { - } + public static TestPixelForAgnosticDecode FromLa16(La16 source) => default; - public void FromRgb24(Rgb24 source) - { - } + public static TestPixelForAgnosticDecode FromLa32(La32 source) => default; - public void FromRgba32(Rgba32 source) - { - } + public static TestPixelForAgnosticDecode FromRgb24(Rgb24 source) => default; - public void ToRgba32(ref Rgba32 dest) - { - } + public static TestPixelForAgnosticDecode FromRgba32(Rgba32 source) => default; - public void FromRgb48(Rgb48 source) - { - } + public static TestPixelForAgnosticDecode FromRgb48(Rgb48 source) => default; - public void FromRgba64(Rgba64 source) - { - } + public static TestPixelForAgnosticDecode FromRgba64(Rgba64 source) => default; public bool Equals(TestPixelForAgnosticDecode other) => false; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index ccb149ca3..813ed505d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -14,9 +14,7 @@ public abstract partial class TestImageProvider : IXunitSerializable } public virtual TPixel GetExpectedBasicTestPatternPixelAt(int x, int y) - { - throw new NotSupportedException("GetExpectedBasicTestPatternPixelAt(x,y) only works with BasicTestPattern"); - } + => throw new NotSupportedException("GetExpectedBasicTestPatternPixelAt(x,y) only works with BasicTestPattern"); private class BasicTestPatternProvider : BlankProvider { @@ -41,7 +39,7 @@ public abstract partial class TestImageProvider : IXunitSerializable public override Image GetImage() { - var result = new Image(this.Configuration, this.Width, this.Height); + Image result = new(this.Configuration, this.Width, this.Height); result.ProcessPixelRows(accessor => { int midY = this.Height / 2; @@ -51,16 +49,16 @@ public abstract partial class TestImageProvider : IXunitSerializable { Span row = accessor.GetRowSpan(y); - row.Slice(0, midX).Fill(TopLeftColor); - row.Slice(midX, this.Width - midX).Fill(TopRightColor); + row[..midX].Fill(TopLeftColor); + row[midX..this.Width].Fill(TopRightColor); } for (int y = midY; y < this.Height; y++) { Span row = accessor.GetRowSpan(y); - row.Slice(0, midX).Fill(BottomLeftColor); - row.Slice(midX, this.Width - midX).Fill(BottomRightColor); + row[..midX].Fill(BottomLeftColor); + row[midX..this.Width].Fill(BottomRightColor); } }); @@ -76,17 +74,10 @@ public abstract partial class TestImageProvider : IXunitSerializable { return x < midX ? TopLeftColor : TopRightColor; } - else - { - return x < midX ? BottomLeftColor : BottomRightColor; - } - } - private static TPixel GetBottomRightColor() - { - TPixel bottomRightColor = default; - bottomRightColor.FromScaledVector4(new Vector4(1f, 0f, 1f, 0.5f)); - return bottomRightColor; + return x < midX ? BottomLeftColor : BottomRightColor; } + + private static TPixel GetBottomRightColor() => TPixel.FromScaledVector4(new Vector4(1f, 0f, 1f, 0.5f)); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 63307f7e2..405d048fc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; @@ -15,19 +16,19 @@ public abstract partial class TestImageProvider : IXunitSerializable /// private class TestPatternProvider : BlankProvider { - private static readonly Dictionary> TestImages = new Dictionary>(); + private static readonly Dictionary> TestImages = []; private static readonly TPixel[] BlackWhitePixels = - { - Color.Black.ToPixel(), - Color.White.ToPixel() - }; + [ + Color.Black.ToPixel(), + Color.White.ToPixel() + ]; private static readonly TPixel[] PinkBluePixels = - { - Color.HotPink.ToPixel(), - Color.Blue.ToPixel() - }; + [ + Color.HotPink.ToPixel(), + Color.Blue.ToPixel() + ]; public TestPatternProvider(int width, int height) : base(width, height) @@ -47,14 +48,15 @@ public abstract partial class TestImageProvider : IXunitSerializable { lock (TestImages) { - if (!TestImages.ContainsKey(this.SourceFileOrDescription)) + if (!TestImages.TryGetValue(this.SourceFileOrDescription, out Image value)) { - var image = new Image(this.Width, this.Height); + Image image = new(this.Width, this.Height); DrawTestPattern(image); - TestImages.Add(this.SourceFileOrDescription, image); + value = image; + TestImages.Add(this.SourceFileOrDescription, value); } - return TestImages[this.SourceFileOrDescription].Clone(this.Configuration); + return value.Clone(this.Configuration); } } @@ -75,12 +77,13 @@ public abstract partial class TestImageProvider : IXunitSerializable /// /// Fills the top right quadrant with alternating solid vertical bars. /// + /// The pixel buffer. private static void VerticalBars(Buffer2D pixels) { // topLeft int left = pixels.Width / 2; int right = pixels.Width; - int top = 0; + const int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 12; if (stride < 1) @@ -96,7 +99,7 @@ public abstract partial class TestImageProvider : IXunitSerializable if (x % stride == 0) { p++; - p = p % PinkBluePixels.Length; + p %= PinkBluePixels.Length; } pixels[x, y] = PinkBluePixels[p]; @@ -107,12 +110,13 @@ public abstract partial class TestImageProvider : IXunitSerializable /// /// fills the top left quadrant with a black and white checker board. /// + /// The pixel buffer. private static void BlackWhiteChecker(Buffer2D pixels) { // topLeft - int left = 0; + const int left = 0; int right = pixels.Width / 2; - int top = 0; + const int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 6; @@ -122,63 +126,62 @@ public abstract partial class TestImageProvider : IXunitSerializable if (y % stride is 0) { p++; - p = p % BlackWhitePixels.Length; + p %= BlackWhitePixels.Length; } - int pstart = p; + int pStart = p; for (int x = left; x < right; x++) { if (x % stride is 0) { p++; - p = p % BlackWhitePixels.Length; + p %= BlackWhitePixels.Length; } pixels[x, y] = BlackWhitePixels[p]; } - p = pstart; + p = pStart; } } /// /// Fills the bottom left quadrant with 3 horizontal bars in Red, Green and Blue with a alpha gradient from left (transparent) to right (solid). /// + /// The pixel buffer private static void TransparentGradients(Buffer2D pixels) { // topLeft - int left = 0; + const int left = 0; int right = pixels.Width / 2; int top = pixels.Height / 2; int bottom = pixels.Height; int height = (int)Math.Ceiling(pixels.Height / 6f); - var red = Color.Red.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern - var green = Color.Green.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern - var blue = Color.Blue.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern - - var c = default(TPixel); + Vector4 red = Color.Red.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern + Vector4 green = Color.Green.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern + Vector4 blue = Color.Blue.ToPixel().ToVector4(); // use real color so we can see how it translates in the test pattern for (int x = left; x < right; x++) { - blue.W = red.W = green.W = (float)x / (float)right; + blue.W = red.W = green.W = x / (float)right; - c.FromVector4(red); + TPixel c = TPixel.FromVector4(red); int topBand = top; for (int y = topBand; y < top + height; y++) { pixels[x, y] = c; } - topBand = topBand + height; - c.FromVector4(green); + topBand += height; + c = TPixel.FromVector4(green); for (int y = topBand; y < topBand + height; y++) { pixels[x, y] = c; } - topBand = topBand + height; - c.FromVector4(blue); + topBand += height; + c = TPixel.FromVector4(blue); for (int y = topBand; y < bottom; y++) { pixels[x, y] = c; @@ -190,6 +193,7 @@ public abstract partial class TestImageProvider : IXunitSerializable /// Fills the bottom right quadrant with all the colors producible by converting iterating over a uint and unpacking it. /// A better algorithm could be used but it works /// + /// The pixel buffer. private static void Rainbow(Buffer2D pixels) { int left = pixels.Width / 2; @@ -199,19 +203,14 @@ public abstract partial class TestImageProvider : IXunitSerializable int pixelCount = left * top; uint stepsPerPixel = (uint)(uint.MaxValue / pixelCount); - TPixel c = default; - var t = new Rgba32(0); + Rgba32 t = default; for (int x = left; x < right; x++) { for (int y = top; y < bottom; y++) { t.PackedValue += stepsPerPixel; - var v = t.ToVector4(); - - // v.W = (x - left) / (float)left; - c.FromVector4(v); - pixels[x, y] = c; + pixels[x, y] = TPixel.FromRgba32(t); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 2243c852d..78e5c9020 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Globalization; using System.Reflection; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -62,7 +63,7 @@ public class ImagingTestCaseUtility extension = ".bmp"; } - extension = extension.ToLower(); + extension = extension.ToLower(CultureInfo.InvariantCulture); if (extension[0] != '.') { @@ -86,7 +87,7 @@ public class ImagingTestCaseUtility } } - details = details ?? string.Empty; + details ??= string.Empty; if (details != string.Empty) { details = '_' + details; @@ -277,8 +278,7 @@ public class ImagingTestCaseUtility where TPixel : unmanaged, IPixel { TPixel pixel = img[x, y]; - Rgba64 rgbaPixel = default; - rgbaPixel.FromScaledVector4(pixel.ToScaledVector4()); + Rgba64 rgbaPixel = Rgba64.FromScaledVector4(pixel.ToScaledVector4()); ushort change = (ushort)Math.Round((perChannelChange / 255F) * 65535F); if (rgbaPixel.R + perChannelChange <= 255) @@ -317,7 +317,6 @@ public class ImagingTestCaseUtility rgbaPixel.A -= perChannelChange; } - pixel.FromRgba64(rgbaPixel); - img[x, y] = pixel; + img[x, y] = TPixel.FromRgba64(rgbaPixel); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 3c74b4893..5da12f264 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -692,14 +692,14 @@ public static class TestImageExtensions this TestImageProvider provider) where TPixel : unmanaged, IPixel { - var allocator = new TestMemoryAllocator(); + TestMemoryAllocator allocator = new(); provider.Configuration.MemoryAllocator = allocator; return new AllocatorBufferCapacityConfigurator(allocator, Unsafe.SizeOf()); } internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) { - var image = new Image(buffer.Width, buffer.Height); + Image image = new(buffer.Width, buffer.Height); Assert.True(image.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out Memory pixelMem)); Span pixels = pixelMem.Span; @@ -708,8 +708,7 @@ public static class TestImageExtensions for (int i = 0; i < bufferSpan.Length; i++) { float value = bufferSpan[i] * scale; - var v = new Vector4(value, value, value, 1f); - pixels[i].FromVector4(v); + pixels[i] = Rgba32.FromVector4(new Vector4(value, value, value, 1f)); } return image; @@ -735,7 +734,7 @@ public static class TestImageExtensions Rectangle sourceRectangle = this.SourceRectangle; Configuration configuration = this.Configuration; - var operation = new RowOperation(configuration, sourceRectangle, source.PixelBuffer); + RowOperation operation = new(configuration, sourceRectangle, source.PixelBuffer); ParallelRowIterator.IterateRowIntervals( configuration, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index fb879c769..f0344e2b9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -35,14 +35,9 @@ public class TestPixel : IXunitSerializable public float Alpha { get; set; } - public TPixel AsPixel() - { - var pix = default(TPixel); - pix.FromScaledVector4(new Vector4(this.Red, this.Green, this.Blue, this.Alpha)); - return pix; - } + public TPixel AsPixel() => TPixel.FromScaledVector4(new Vector4(this.Red, this.Green, this.Blue, this.Alpha)); - internal Span AsSpan() => new(new[] { this.AsPixel() }); + internal Span AsSpan() => new([this.AsPixel()]); public void Deserialize(IXunitSerializationInfo info) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 0b792b7fb..fbdfb9d61 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -19,11 +19,11 @@ namespace SixLabors.ImageSharp.Tests; /// public static class TestUtils { - private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); + private static readonly Dictionary ClrTypes2PixelTypes = new(); private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; - private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary(); + private static readonly Dictionary PixelTypes2ClrTypes = new(); private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes() .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) @@ -52,7 +52,7 @@ public static class TestUtils public static byte[] GetRandomBytes(int length, int seed = 42) { - var rnd = new Random(42); + Random rnd = new(seed); byte[] bytes = new byte[length]; rnd.NextBytes(bytes); return bytes; @@ -60,7 +60,7 @@ public static class TestUtils internal static byte[] FillImageWithRandomBytes(Image image) { - byte[] expected = TestUtils.GetRandomBytes(image.Width * image.Height * 2); + byte[] expected = GetRandomBytes(image.Width * image.Height * 2); image.ProcessPixelRows(accessor => { int cnt = 0; @@ -84,9 +84,6 @@ public static class TestUtils return false; } - var rgb1 = default(Rgb24); - var rgb2 = default(Rgb24); - Buffer2D pixA = a.GetRootFramePixelBuffer(); Buffer2D pixB = b.GetRootFramePixelBuffer(); for (int y = 0; y < a.Height; y++) @@ -105,11 +102,11 @@ public static class TestUtils } else { - Rgba32 rgba = default; - ca.ToRgba32(ref rgba); - rgb1 = rgba.Rgb; - cb.ToRgba32(ref rgba); - rgb2 = rgba.Rgb; + Rgba32 rgba = ca.ToRgba32(); + Rgb24 rgb1 = rgba.Rgb; + + rgba = cb.ToRgba32(); + Rgb24 rgb2 = rgba.Rgb; if (!rgb1.Equals(rgb2)) { @@ -144,7 +141,7 @@ public static class TestUtils return PixelTypes2ClrTypes; } - var result = new Dictionary(); + Dictionary result = new(); foreach (PixelTypes pt in AllConcretePixelTypes) { if (pixelTypes.HasAll(pt)) @@ -167,7 +164,7 @@ public static class TestUtils internal static Color GetColorByName(string colorName) { - var f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; + FieldInfo f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; return (Color)f.GetValue(null); } @@ -188,7 +185,7 @@ public static class TestUtils int width = expected.Width; expected.Mutate(process); - var allocator = new TestMemoryAllocator(); + TestMemoryAllocator allocator = new(); provider.Configuration.MemoryAllocator = allocator; allocator.BufferCapacityInBytes = bufferCapacityInPixelRows * width * Unsafe.SizeOf(); @@ -301,9 +298,9 @@ public static class TestUtils using (Image image0 = provider.GetImage()) { Assert.True(image0.DangerousTryGetSinglePixelMemory(out Memory imageMem)); - var mmg = TestMemoryManager.CreateAsCopyOf(imageMem.Span); + TestMemoryManager mmg = TestMemoryManager.CreateAsCopyOf(imageMem.Span); - using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) + using (Image image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) { image1.Mutate(process); image1.DebugSave( @@ -353,7 +350,7 @@ public static class TestUtils using (Image image = provider.GetImage()) { - var bounds = new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2); + Rectangle bounds = new(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2); image.Mutate(x => process(x, bounds)); image.DebugSave(provider, testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); image.CompareToReferenceOutput(comparer, provider, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); @@ -384,7 +381,7 @@ public static class TestUtils if (property is null) { - throw new Exception($"No resampler named '{name}"); + throw new InvalidOperationException($"No resampler named '{name}"); } return (IResampler)property.GetValue(null); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 3dceaf252..2f5291131 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Collections.Concurrent; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -302,12 +301,11 @@ public class TestImageProviderTests Assert.Equal(20, img.Height); Buffer2D pixels = img.GetRootFramePixelBuffer(); - Rgba32 rgba = default; for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) { - pixels[x, y].ToRgba32(ref rgba); + Rgba32 rgba = pixels[x, y].ToRgba32(); Assert.Equal(255, rgba.R); Assert.Equal(100, rgba.G); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 33ee68068..46fb7159e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -21,20 +21,17 @@ public class TestUtilityExtensionsTests public static Image CreateTestImage() where TPixel : unmanaged, IPixel { - var image = new Image(10, 10); + Image image = new(10, 10); Buffer2D pixels = image.GetRootFramePixelBuffer(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { - var v = new Vector4(i, j, 0, 1); + Vector4 v = new(i, j, 0, 1); v /= 10; - var color = default(TPixel); - color.FromVector4(v); - - pixels[i, j] = color; + pixels[i, j] = TPixel.FromVector4(v); } } From 8af75fe0d4ab187e7de9ca49507ec84d99eb01f2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 21 Jan 2024 21:42:33 +1000 Subject: [PATCH 056/220] Restore GetPixelTypeInfo --- src/ImageSharp/PixelFormats/IPixel.cs | 13 ++++++------- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 4 ---- tests/ImageSharp.Tests/Issues/Issue594.cs | 12 +++--------- .../PixelOperations/PixelOperationsTests.cs | 4 ++-- .../TestUtilities/ApproximateFloatComparer.cs | 9 --------- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index f473ce248..23b18354a 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -22,13 +22,6 @@ public interface IPixel : IPixel, IEquatable PixelOperations CreatePixelOperations(); #pragma warning disable CA1000 // Do not declare static members on generic types - - /// - /// Gets the pixel type information. - /// - /// The . - static abstract PixelTypeInfo GetPixelTypeInfo(); - /// /// Initializes the pixel instance from a generic scaled . /// @@ -141,6 +134,12 @@ public interface IPixel : IPixel, IEquatable /// public interface IPixel { + /// + /// Gets the pixel type information. + /// + /// The . + static abstract PixelTypeInfo GetPixelTypeInfo(); + /// /// Convert the pixel instance into representation. /// diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index e0c65475a..a389c8ab8 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -80,9 +80,5 @@ - - - - diff --git a/tests/ImageSharp.Tests/Issues/Issue594.cs b/tests/ImageSharp.Tests/Issues/Issue594.cs index 23a3913fa..7f976a373 100644 --- a/tests/ImageSharp.Tests/Issues/Issue594.cs +++ b/tests/ImageSharp.Tests/Issues/Issue594.cs @@ -8,9 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Issues; public class Issue594 { - // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue - // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact] // (Skip = "Skipped because of issue #594")] + [Fact] public void NormalizedByte4Test() { // Test PackedValue @@ -50,9 +48,7 @@ public class Issue594 Assert.Equal(958796544U, new NormalizedByte4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); } - // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue - // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact] //(Skip = "Skipped because of issue #594")] + [Fact] public void NormalizedShort4Test() { // Test PackedValue @@ -87,9 +83,7 @@ public class Issue594 Assert.Equal(4150390751449251866UL, new NormalizedShort4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); } - // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue - // see https://github.com/SixLabors/ImageSharp/issues/594 - [Fact] // (Skip = "Skipped because of issue #594")] + [Fact] public void Short4Test() { // Test the limits. diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index b9d9b695a..68c282d8a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -1216,7 +1216,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { - Assert.Equal((IPixel)expected[i], (IPixel)actual[i], comparer); + Assert.Equal(((IPixel)expected[i]).ToScaledVector4(), ((IPixel)actual[i]).ToScaledVector4(), comparer); } } else @@ -1231,7 +1231,7 @@ public abstract class PixelOperationsTests : MeasureFixture } } - // TODO: We really need a PixelTypeInfo.BitsPerComponent property!! + // TODO: Figure out a means to use PixelTypeInfo here. private static bool IsComplexPixel() => default(TDest) switch { HalfSingle or HalfVector2 or L16 or La32 or NormalizedShort2 or Rg32 or Short2 => true, diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index e35f36fee..21ac6966b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; @@ -14,7 +13,6 @@ namespace SixLabors.ImageSharp.Tests; internal readonly struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer, - IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer> @@ -47,13 +45,6 @@ internal readonly struct ApproximateFloatComparer : public int GetHashCode(Vector2 obj) => obj.GetHashCode(); - /// - public bool Equals(IPixel x, IPixel y) - => this.Equals(x.ToScaledVector4(), y.ToScaledVector4()); - - public int GetHashCode(IPixel obj) - => obj.ToScaledVector4().GetHashCode(); - /// public bool Equals(Vector4 x, Vector4 y) => this.Equals(x.X, y.X) From 2de4f2c54ed0bcadc0c7c305a55d8463a31ba8fd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 21 Jan 2024 22:04:47 +1000 Subject: [PATCH 057/220] Update refs --- .editorconfig | 2 ++ shared-infrastructure | 2 +- src/ImageSharp.ruleset | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2e3045fb1..c28089d72 100644 --- a/.editorconfig +++ b/.editorconfig @@ -172,6 +172,8 @@ dotnet_diagnostic.IDE0063.severity = suggestion csharp_using_directive_placement = outside_namespace:warning # Modifier preferences csharp_prefer_static_local_function = true:warning +# Primary constructor preferences +csharp_style_prefer_primary_constructors = false:none ########################################## # Unnecessary Code Rules diff --git a/shared-infrastructure b/shared-infrastructure index d65232bbb..1dbfb576c 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit d65232bbbfe55a9a153b4058139dda5230e6eb4f +Subproject commit 1dbfb576c83507645265c79e03369b66cdc0379f diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset index 72e0cd1b0..b60989020 100644 --- a/src/ImageSharp.ruleset +++ b/src/ImageSharp.ruleset @@ -1,7 +1,4 @@  - - - \ No newline at end of file From 6580494b5aff13480f81ed623b14b5c3c1eeba62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:43:09 +0000 Subject: [PATCH 058/220] Bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 6 +++--- .github/workflows/code-coverage.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 75bcb8a25..57488a1d0 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -67,7 +67,7 @@ jobs: run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - name: Git Setup LFS Cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: lfs-cache with: path: .git/lfs @@ -80,7 +80,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: nuget-cache with: path: ~/.nuget @@ -162,7 +162,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: nuget-cache with: path: ~/.nuget diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 62b6477ee..f9d2da0c8 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -34,7 +34,7 @@ jobs: run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - name: Git Setup LFS Cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: lfs-cache with: path: .git/lfs @@ -47,7 +47,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: nuget-cache with: path: ~/.nuget From 7b93ee74d5b881daa51b5c8c6dc9e657ba14f83b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 23 Jan 2024 23:26:23 +1000 Subject: [PATCH 059/220] Update benchmarks --- .../General/PixelConversion/ITestPixel.cs | 8 +- .../PixelConversion_ConvertFromRgba32.cs | 184 +++++++++++------- .../PixelConversion_ConvertFromVector4.cs | 37 ---- .../General/PixelConversion/TestArgb.cs | 95 +++++---- .../General/PixelConversion/TestRgba.cs | 69 ++++--- .../General/PixelConversion/TestRgbaVector.cs | 51 +++++ 6 files changed, 266 insertions(+), 178 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs index 8820406af..82f0da505 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs @@ -11,19 +11,23 @@ public interface ITestPixel { void FromRgba32(Rgba32 source); + static abstract T StaticFromRgba32(Rgba32 source); + void FromRgba32(ref Rgba32 source); void FromBytes(byte r, byte g, byte b, byte a); void FromVector4(Vector4 source); + static abstract T StaticFromVector4(Vector4 source); + void FromVector4(ref Vector4 source); Rgba32 ToRgba32(); - void CopyToRgba32(ref Rgba32 dest); + void CopyToRgba32(ref Rgba32 destination); Vector4 ToVector4(); - void CopyToVector4(ref Vector4 dest); + void CopyToVector4(ref Vector4 destination); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 479079a80..1d83b94dc 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -13,25 +13,25 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; public abstract class PixelConversion_ConvertFromRgba32 { - internal struct ConversionRunner + internal readonly struct ConversionRunner where T : struct, ITestPixel { - public readonly T[] Dest; + public readonly T[] Destination; public readonly Rgba32[] Source; public ConversionRunner(int count) { - this.Dest = new T[count]; + this.Destination = new T[count]; this.Source = new Rgba32[count]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByRefConversion() + public readonly void RunByRefConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -41,11 +41,11 @@ public abstract class PixelConversion_ConvertFromRgba32 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByValConversion() + public readonly void RunByValConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -55,11 +55,25 @@ public abstract class PixelConversion_ConvertFromRgba32 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunFromBytesConversion() + public readonly void RunStaticByValConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; + ref Rgba32 sourceBaseRef = ref this.Source[0]; + + for (nuint i = 0; i < (uint)count; i++) + { + Unsafe.Add(ref destBaseRef, i) = T.StaticFromRgba32(Unsafe.Add(ref sourceBaseRef, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void RunFromBytesConversion() + { + int count = this.Destination.Length; + + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -74,6 +88,8 @@ public abstract class PixelConversion_ConvertFromRgba32 internal ConversionRunner PermutedRunnerRgbaToArgb; + internal ConversionRunner RunnerRgbaToRgbaVector; + [Params(256, 2048)] public int Count { get; set; } @@ -82,34 +98,29 @@ public abstract class PixelConversion_ConvertFromRgba32 { this.CompatibleMemLayoutRunner = new ConversionRunner(this.Count); this.PermutedRunnerRgbaToArgb = new ConversionRunner(this.Count); + this.RunnerRgbaToRgbaVector = new ConversionRunner(this.Count); } } public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_ConvertFromRgba32 { [Benchmark(Baseline = true)] - public void ByRef() - { - this.CompatibleMemLayoutRunner.RunByRefConversion(); - } + public void ByRef() => this.CompatibleMemLayoutRunner.RunByRefConversion(); [Benchmark] - public void ByVal() - { - this.CompatibleMemLayoutRunner.RunByValConversion(); - } + public void ByVal() => this.CompatibleMemLayoutRunner.RunByValConversion(); [Benchmark] - public void FromBytes() - { - this.CompatibleMemLayoutRunner.RunFromBytesConversion(); - } + public void StaticByVal() => this.CompatibleMemLayoutRunner.RunStaticByValConversion(); + + [Benchmark] + public void FromBytes() => this.CompatibleMemLayoutRunner.RunFromBytesConversion(); [Benchmark] public void Inline() { ref Rgba32 sBase = ref this.CompatibleMemLayoutRunner.Source[0]; - ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Dest[0]); + ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Destination[0]); for (nuint i = 0; i < (uint)this.Count; i++) { @@ -120,50 +131,46 @@ public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_Conv /* BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores - .NET SDK 8.0.100 - [Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 - - - | Method | Count | Mean | Error | StdDev | Ratio | - |---------- |------ |-----------:|--------:|--------:|------:| - | ByRef | 256 | 102.5 ns | 0.44 ns | 0.39 ns | 1.00 | - | ByVal | 256 | 102.2 ns | 0.30 ns | 0.25 ns | 1.00 | - | FromBytes | 256 | 200.5 ns | 1.01 ns | 0.90 ns | 1.96 | - | Inline | 256 | 107.0 ns | 0.90 ns | 0.84 ns | 1.04 | - | | | | | | | - | ByRef | 2048 | 770.8 ns | 3.22 ns | 2.86 ns | 1.00 | - | ByVal | 2048 | 770.3 ns | 2.05 ns | 1.92 ns | 1.00 | - | FromBytes | 2048 | 1,546.8 ns | 7.51 ns | 6.66 ns | 2.01 | - | Inline | 2048 | 797.6 ns | 2.90 ns | 2.26 ns | 1.03 | + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | + |------------ |------ |-----------:|--------:|--------:|------:| + | ByRef | 256 | 103.4 ns | 0.52 ns | 0.46 ns | 1.00 | + | ByVal | 256 | 103.3 ns | 1.48 ns | 1.38 ns | 1.00 | + | StaticByVal | 256 | 104.0 ns | 0.36 ns | 0.30 ns | 1.01 | + | FromBytes | 256 | 201.8 ns | 1.30 ns | 1.15 ns | 1.95 | + | Inline | 256 | 106.6 ns | 0.40 ns | 0.34 ns | 1.03 | + | | | | | | | + | ByRef | 2048 | 771.5 ns | 3.68 ns | 3.27 ns | 1.00 | + | ByVal | 2048 | 769.7 ns | 3.39 ns | 2.83 ns | 1.00 | + | StaticByVal | 2048 | 773.2 ns | 3.95 ns | 3.50 ns | 1.00 | + | FromBytes | 2048 | 1,555.3 ns | 9.24 ns | 8.19 ns | 2.02 | + | Inline | 2048 | 799.5 ns | 5.91 ns | 4.93 ns | 1.04 | */ } public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 { [Benchmark(Baseline = true)] - public void ByRef() - { - this.PermutedRunnerRgbaToArgb.RunByRefConversion(); - } + public void ByRef() => this.PermutedRunnerRgbaToArgb.RunByRefConversion(); [Benchmark] - public void ByVal() - { - this.PermutedRunnerRgbaToArgb.RunByValConversion(); - } + public void ByVal() => this.PermutedRunnerRgbaToArgb.RunByValConversion(); [Benchmark] - public void FromBytes() - { - this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); - } + public void StaticByVal() => this.PermutedRunnerRgbaToArgb.RunStaticByValConversion(); + + [Benchmark] + public void FromBytes() => this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); [Benchmark] public void InlineShuffle() { ref Rgba32 sBase = ref this.PermutedRunnerRgbaToArgb.Source[0]; - ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Dest[0]; + ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Destination[0]; for (nuint i = 0; i < (uint)this.Count; i++) { @@ -181,25 +188,64 @@ public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConver public void PixelConverter_Rgba32_ToArgb32() { Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); - Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Dest); + Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Destination); PixelConverter.FromRgba32.ToArgb32(source, dest); } /* - RESULTS: - | Method | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | - |------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:| - | ByRef | 256 | 288.84 ns | 19.601 ns | 52.319 ns | 268.10 ns | 1.00 | 0.00 | - | ByVal | 256 | 267.97 ns | 1.831 ns | 1.713 ns | 267.85 ns | 0.77 | 0.18 | - | FromBytes | 256 | 266.81 ns | 2.427 ns | 2.270 ns | 266.47 ns | 0.76 | 0.18 | - | InlineShuffle | 256 | 291.41 ns | 5.820 ns | 5.444 ns | 290.17 ns | 0.83 | 0.19 | - | PixelConverter_Rgba32_ToArgb32 | 256 | 38.62 ns | 0.431 ns | 0.403 ns | 38.68 ns | 0.11 | 0.03 | - | | | | | | | | | - | ByRef | 2048 | 2,197.69 ns | 15.826 ns | 14.804 ns | 2,197.25 ns | 1.00 | 0.00 | - | ByVal | 2048 | 2,226.81 ns | 44.266 ns | 62.054 ns | 2,197.17 ns | 1.03 | 0.04 | - | FromBytes | 2048 | 2,181.35 ns | 18.033 ns | 16.868 ns | 2,185.97 ns | 0.99 | 0.01 | - | InlineShuffle | 2048 | 2,233.10 ns | 27.673 ns | 24.531 ns | 2,229.78 ns | 1.02 | 0.01 | - | PixelConverter_Rgba32_ToArgb32 | 2048 | 139.90 ns | 2.152 ns | 3.825 ns | 138.70 ns | 0.06 | 0.00 | + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | + |------------------------------- |------ |------------:|----------:|---------:|------:|--------:| + | ByRef | 256 | 203.48 ns | 3.318 ns | 3.104 ns | 1.00 | 0.00 | + | ByVal | 256 | 201.46 ns | 2.242 ns | 1.872 ns | 0.99 | 0.02 | + | StaticByVal | 256 | 201.45 ns | 0.791 ns | 0.701 ns | 0.99 | 0.02 | + | FromBytes | 256 | 200.76 ns | 1.365 ns | 1.140 ns | 0.99 | 0.01 | + | InlineShuffle | 256 | 221.65 ns | 2.104 ns | 1.968 ns | 1.09 | 0.02 | + | PixelConverter_Rgba32_ToArgb32 | 256 | 26.23 ns | 0.277 ns | 0.231 ns | 0.13 | 0.00 | + | | | | | | | | + | ByRef | 2048 | 1,561.54 ns | 11.208 ns | 8.751 ns | 1.00 | 0.00 | + | ByVal | 2048 | 1,554.26 ns | 9.607 ns | 8.517 ns | 1.00 | 0.01 | + | StaticByVal | 2048 | 1,562.48 ns | 8.937 ns | 8.360 ns | 1.00 | 0.01 | + | FromBytes | 2048 | 1,552.68 ns | 7.445 ns | 5.812 ns | 0.99 | 0.01 | + | InlineShuffle | 2048 | 1,711.28 ns | 7.559 ns | 6.312 ns | 1.10 | 0.01 | + | PixelConverter_Rgba32_ToArgb32 | 2048 | 94.43 ns | 0.363 ns | 0.322 ns | 0.06 | 0.00 | + */ +} + +public class PixelConversion_ConvertFromRgba32_RgbaToRgbaVector : PixelConversion_ConvertFromRgba32 +{ + [Benchmark(Baseline = true)] + public void ByRef() => this.RunnerRgbaToRgbaVector.RunByRefConversion(); + + [Benchmark] + public void ByVal() => this.RunnerRgbaToRgbaVector.RunByValConversion(); + + [Benchmark] + public void StaticByVal() => this.RunnerRgbaToRgbaVector.RunStaticByValConversion(); + + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | + |------------ |------ |-----------:|---------:|---------:|------:|--------:| + | ByRef | 256 | 448.5 ns | 4.86 ns | 4.06 ns | 1.00 | 0.00 | + | ByVal | 256 | 447.0 ns | 1.55 ns | 1.21 ns | 1.00 | 0.01 | + | StaticByVal | 256 | 447.4 ns | 1.67 ns | 1.30 ns | 1.00 | 0.01 | + | | | | | | | | + | ByRef | 2048 | 3,577.7 ns | 53.80 ns | 47.69 ns | 1.00 | 0.00 | + | ByVal | 2048 | 3,590.5 ns | 43.59 ns | 36.40 ns | 1.00 | 0.02 | + | StaticByVal | 2048 | 3,604.6 ns | 16.19 ns | 14.36 ns | 1.01 | 0.01 | */ } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs index dd85d0641..57f79ba1f 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs @@ -13,43 +13,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; public class PixelConversion_ConvertFromVector4 { - [StructLayout(LayoutKind.Sequential)] - private struct TestRgbaVector : ITestPixel - { - private Vector4 v; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) - { - this.v = p; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) - { - this.v = p; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => this.v; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) - { - dest = this.v; - } - - public void FromRgba32(Rgba32 source) => throw new System.NotImplementedException(); - - public void FromRgba32(ref Rgba32 source) => throw new System.NotImplementedException(); - - public void FromBytes(byte r, byte g, byte b, byte a) => throw new System.NotImplementedException(); - - public Rgba32 ToRgba32() => throw new System.NotImplementedException(); - - public void CopyToRgba32(ref Rgba32 dest) => throw new System.NotImplementedException(); - } - private struct ConversionRunner where T : struct, ITestPixel { diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs index 84698a0e1..21bef5a15 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -16,26 +17,20 @@ public struct TestArgb : ITestPixel public byte G; public byte B; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 p) - { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; - } + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 p) + private TestArgb(Rgba32 source) { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBytes(byte r, byte g, byte b, byte a) + private TestArgb(byte r, byte g, byte b, byte a) { this.R = r; this.G = g; @@ -44,50 +39,70 @@ public struct TestArgb : ITestPixel } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) + public void FromRgba32(Rgba32 source) { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) + public void FromRgba32(ref Rgba32 source) { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return new Rgba32(this.R, this.G, this.B, this.A); - } + public static TestArgb StaticFromRgba32(Rgba32 source) => new(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) + public void FromBytes(byte r, byte g, byte b, byte a) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + this.R = r; + this.G = g; + this.B = b; + this.A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() + public void FromVector4(Vector4 source) => this = Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestArgb StaticFromVector4(Vector4 source) => Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 source) => this = Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToRgba32(ref Rgba32 destination) { - return new Vector4(this.R, this.G, this.B, this.A); + destination.R = this.R; + destination.G = this.G; + destination.B = this.B; + destination.A = this.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.ToVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TestArgb Pack(Vector4 vector) { - dest.X = this.R; - dest.Y = this.G; - dest.Z = this.B; - dest.W = this.A; + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs index 76449c9d9..1499fb7d3 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -16,17 +17,29 @@ public struct TestRgba : ITestPixel public byte B; public byte A; + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) + private TestRgba(byte r, byte g, byte b, byte a) { - this = Unsafe.As(ref source); + this.R = r; + this.G = g; + this.B = b; + this.A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 source) - { - this = Unsafe.As(ref source); - } + private TestRgba(Rgba32 source) => this = Unsafe.As(ref source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this = Unsafe.As(ref source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgba StaticFromRgba32(Rgba32 source) => new(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) => this = Unsafe.As(ref source); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBytes(byte r, byte g, byte b, byte a) @@ -37,39 +50,35 @@ public struct TestRgba : ITestPixel this.A = a; } - public void FromVector4(Vector4 source) - { - throw new System.NotImplementedException(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 source) => this = Pack(source); - public void FromVector4(ref Vector4 source) - { - throw new System.NotImplementedException(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgba StaticFromVector4(Vector4 source) => Pack(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return Unsafe.As(ref this); - } + public void FromVector4(ref Vector4 source) => this = Pack(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) - { - dest = Unsafe.As(ref this); - } + public Rgba32 ToRgba32() => Unsafe.As(ref this); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A) * new Vector4(1f / 255f); - } + public void CopyToRgba32(ref Rgba32 destination) => destination = Unsafe.As(ref this); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.ToVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TestRgba Pack(Vector4 vector) { - var tmp = new Vector4(this.R, this.G, this.B, this.A); - tmp *= new Vector4(1f / 255f); - dest = tmp; + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs new file mode 100644 index 000000000..07790ae99 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +[StructLayout(LayoutKind.Sequential)] +public struct TestRgbaVector : ITestPixel +{ + private Vector4 v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TestRgbaVector(Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgbaVector StaticFromVector4(Vector4 source) => new(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => this.v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.v = source.ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgbaVector StaticFromRgba32(Rgba32 source) => new(source.ToScaledVector4()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) => this.v = source.ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) => this.v = new Rgba32(r, g, b, a).ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.v); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToRgba32(ref Rgba32 destination) => destination = Rgba32.FromScaledVector4(this.v); +} From 3a3ff89ec48d9df4150429099a5ec91db82708b0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 23 Jan 2024 23:48:30 +1000 Subject: [PATCH 060/220] Make CreatePixelOperations static --- src/ImageSharp/PixelFormats/IPixel.cs | 4 ++-- src/ImageSharp/PixelFormats/PixelImplementations/A8.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs | 2 +- .../PixelFormats/PixelImplementations/HalfSingle.cs | 2 +- .../PixelFormats/PixelImplementations/HalfVector2.cs | 2 +- .../PixelFormats/PixelImplementations/HalfVector4.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/L16.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/L8.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/La16.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/La32.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedByte2.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedByte4.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedShort2.cs | 2 +- .../PixelFormats/PixelImplementations/NormalizedShort4.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs | 2 +- .../PixelFormats/PixelImplementations/Rgba1010102.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs | 2 +- .../PixelFormats/PixelImplementations/RgbaVector.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs | 2 +- src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs | 2 +- src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs | 2 +- tests/ImageSharp.Tests/Color/RgbaDouble.cs | 2 +- tests/ImageSharp.Tests/TestFormat.cs | 2 +- 33 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 23b18354a..adf386614 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -14,14 +14,14 @@ namespace SixLabors.ImageSharp.PixelFormats; public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { +#pragma warning disable CA1000 // Do not declare static members on generic types /// /// Creates a instance for this pixel type. /// This method is not intended to be consumed directly. Use instead. /// /// The instance. - PixelOperations CreatePixelOperations(); + static abstract PixelOperations CreatePixelOperations(); -#pragma warning disable CA1000 // Do not declare static members on generic types /// /// Initializes the pixel instance from a generic scaled . /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index fdf7ccb40..94fbf7eb7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -75,7 +75,7 @@ public partial struct A8 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 65354c807..453a39228 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -192,7 +192,7 @@ public partial struct Abgr32 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 50557880a..f8608ecc5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -185,7 +185,7 @@ public partial struct Argb32 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 12e6736b7..a860edc56 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -95,7 +95,7 @@ public partial struct Bgr24 : IPixel PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index fe26bb8fd..87055bf22 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -73,7 +73,7 @@ public partial struct Bgr565(Vector3 vector) : IPixel, IPackedVector - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index fc5d55e64..0fe7e4cc2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -134,7 +134,7 @@ public partial struct Bgra32 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 4c89d8e94..55971210c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -86,7 +86,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index fde937fb5..4c94dea5f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -84,7 +84,7 @@ public partial struct Bgra5551 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 8b4384ff7..680a7ee0b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -86,7 +86,7 @@ public partial struct Byte4 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 45cc061ae..888d992d8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -70,7 +70,7 @@ public partial struct HalfSingle : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 1fd312523..861a8480a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -82,7 +82,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 4741bcf9e..d0b57d788 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -87,7 +87,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 41578a6f6..2b5241b0b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -75,7 +75,7 @@ public partial struct L16 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 90731f6e9..5d733bdbb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -77,7 +77,7 @@ public partial struct L8 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 948f0b144..69ca66218 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -104,7 +104,7 @@ public partial struct La16 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 0381973d9..1886ef39a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -101,7 +101,7 @@ public partial struct La32 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index e901adc9e..9551d7242 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -85,7 +85,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 1202801ab..1fb386725 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -92,7 +92,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index e15cdafa7..a17868f6d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -87,7 +87,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index f692f73b2..2b33fec27 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -93,7 +93,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 0dea02512..e7c97269e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -82,7 +82,7 @@ public partial struct Rg32 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index a4445bd71..ac855d47d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -104,7 +104,7 @@ public partial struct Rgb24 : IPixel PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index d4f1cabb2..e822d2abc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -89,7 +89,7 @@ public partial struct Rgb48 : IPixel PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 0f7be1774..cdee22964 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -84,7 +84,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 8b5779a53..fc347c166 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -288,7 +288,7 @@ public partial struct Rgba32 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 73629461b..27c4752e1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -210,7 +210,7 @@ public partial struct Rgba64 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 60555ad95..a27cffad8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -104,7 +104,7 @@ public partial struct RgbaVector : IPixel PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 043599c36..403d3fbea 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -90,7 +90,7 @@ public partial struct Short2 : IPixel, IPackedVector PixelAlphaRepresentation.None); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index df43703cb..b6cece2be 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -97,7 +97,7 @@ public partial struct Short4 : IPixel, IPackedVector PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 174b3fae1..c769b389d 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.PixelFormats; public partial class PixelOperations where TPixel : unmanaged, IPixel { - private static readonly Lazy> LazyInstance = new(() => default(TPixel).CreatePixelOperations(), true); + private static readonly Lazy> LazyInstance = new(TPixel.CreatePixelOperations, true); /// /// Gets the global instance for the pixel type diff --git a/tests/ImageSharp.Tests/Color/RgbaDouble.cs b/tests/ImageSharp.Tests/Color/RgbaDouble.cs index bb8672941..9a751e879 100644 --- a/tests/ImageSharp.Tests/Color/RgbaDouble.cs +++ b/tests/ImageSharp.Tests/Color/RgbaDouble.cs @@ -104,7 +104,7 @@ public struct RgbaDouble : IPixel PixelAlphaRepresentation.Unassociated); /// - public readonly PixelOperations CreatePixelOperations() => new(); + public static PixelOperations CreatePixelOperations() => new(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 89e2a11a9..10211f386 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -275,7 +275,7 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat PixelColorType.Red | PixelColorType.Green, PixelAlphaRepresentation.None); - public PixelOperations CreatePixelOperations() => new(); + public static PixelOperations CreatePixelOperations() => new(); public static TestPixelForAgnosticDecode FromScaledVector4(Vector4 vector) => default; From 51272021290112a51812c53eefb9d0d4124b13ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Tue, 23 Jan 2024 17:00:34 +0100 Subject: [PATCH 061/220] pr comment fix --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs | 2 +- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index e11923ac8..d6b40fa7f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -523,7 +523,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// The remaining bytes in the segment block. private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize) { - Span temp = stackalloc byte[markerContentByteSize]; + Span temp = new byte[markerContentByteSize]; char[] chars = new char[markerContentByteSize]; JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index cc6042b6e..1b2b4cbb1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -185,7 +185,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals // Length (comment strings lengths) + (comments markers with payload sizes) int commentsBytes = metadata.Comments.Sum(x => x.Length) + (metadata.Comments.Count * 4); int commentStart = 0; - Span commentBuffer = stackalloc byte[commentsBytes]; + Span commentBuffer = new byte[commentsBytes]; foreach (Memory comment in metadata.Comments) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index fa54859a3..bd68eaf20 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -137,7 +137,7 @@ public partial class JpegEncoderTests [MemberData(nameof(QualityFiles))] public void Encode_PreservesQuality(string imagePath, int quality) { - var testFile = TestFile.Create(imagePath); + TestFile testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) @@ -160,7 +160,7 @@ public partial class JpegEncoderTests where TPixel : unmanaged, IPixel { // arrange - using var input = provider.GetImage(JpegDecoder.Instance); + using Image input = provider.GetImage(JpegDecoder.Instance); using var memStream = new MemoryStream(); // act @@ -168,7 +168,7 @@ public partial class JpegEncoderTests // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(1, actual.Comments.Count); @@ -190,7 +190,7 @@ public partial class JpegEncoderTests // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(2, actual.Comments?.Count); From 9260be9d2996c614d7c992540ebef6ab4d2ca7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Thu, 25 Jan 2024 19:19:41 +0100 Subject: [PATCH 062/220] Add com character limit, comment as IList, remove unnecessary extension methods --- .../Formats/Jpeg/JpegEncoderCore.cs | 43 +++++++++++-------- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 2 +- .../Formats/Jpeg/MetadataExtensions.cs | 33 -------------- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 4 +- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 12 +++--- 5 files changed, 34 insertions(+), 60 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 1b2b4cbb1..4dc920207 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -177,39 +177,46 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// The image metadata. private void WriteComment(JpegMetadata metadata) { - if (metadata.Comments is { Count: 0 }) + int maxCommentLength = 65533; + + if (metadata.Comments.Count == 0) { return; } - // Length (comment strings lengths) + (comments markers with payload sizes) - int commentsBytes = metadata.Comments.Sum(x => x.Length) + (metadata.Comments.Count * 4); - int commentStart = 0; - Span commentBuffer = new byte[commentsBytes]; - - foreach (Memory comment in metadata.Comments) + for (int i = 0; i < metadata.Comments.Count; i++) { - int totalComLength = comment.Length + 4; + Memory chars = metadata.Comments[i]; + + if (chars.Length > maxCommentLength) + { + Memory splitComment = chars.Slice(maxCommentLength, chars.Length - maxCommentLength); + metadata.Comments.Insert(i + 1, splitComment); - Span commentData = commentBuffer.Slice(commentStart, totalComLength); - Span markers = commentData.Slice(0, 2); - Span payloadSize = commentData.Slice(2, 2); - Span payload = commentData.Slice(4, comment.Length); + // We don't want to keep the extra bytes + chars = chars.Slice(0, maxCommentLength); + } + + int commentLength = chars.Length + 4; + + Span comment = new byte[commentLength]; + Span markers = comment.Slice(0, 2); + Span payloadSize = comment.Slice(2, 2); + Span payload = comment.Slice(4, chars.Length); // Beginning of comment ff fe markers[0] = JpegConstants.Markers.XFF; markers[1] = JpegConstants.Markers.COM; // Write payload size - BinaryPrimitives.WriteInt16BigEndian(payloadSize, (short)(commentData.Length - 2)); + int comWithoutMarker = commentLength - 2; + payloadSize[0] = (byte)((comWithoutMarker >> 8) & 0xFF); + payloadSize[1] = (byte)(comWithoutMarker & 0xFF); - Encoding.ASCII.GetBytes(comment.Span, payload); + Encoding.ASCII.GetBytes(chars.Span, payload); - // Indicate begin of next comment in buffer - commentStart += totalComLength; + this.outputStream.Write(comment, 0, comment.Length); } - - this.outputStream.Write(commentBuffer, 0, commentBuffer.Length); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 61fe3b214..bf758dfd0 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -106,7 +106,7 @@ public class JpegMetadata : IDeepCloneable /// /// Gets the comments. /// - public ICollection>? Comments { get; } + public IList> Comments { get; } /// public IDeepCloneable DeepClone() => new JpegMetadata(this); diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs index 0c66fcbdd..7330e74b7 100644 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs @@ -18,37 +18,4 @@ public static partial class MetadataExtensions /// The metadata this method extends. /// The . public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); - - /// - /// Sets the comment in - /// - /// The metadata this method extends. - /// The index of comment to be inserted to. - /// The comment string. - public static void SetComment(this JpegMetadata metadata, int index, string comment) - { - if (metadata.Comments == null) - { - return; - } - - ASCIIEncoding encoding = new(); - byte[] bytes = encoding.GetBytes(comment); - List>? comments = metadata.Comments as List>; - comments?.Insert(index, encoding.GetChars(bytes)); - } - - /// - /// Gets the comments from - /// - /// The metadata this method extends. - /// The index of comment. - /// The IEnumerable string of comments. - public static string? GetComment(this JpegMetadata metadata, int index) => metadata.Comments?.ElementAtOrDefault(index).ToString(); - - /// - /// Clears comments - /// - /// The . - public static void ClearComments(this JpegMetadata metadata) => metadata.Comments?.Clear(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index fb37a956d..369e71abf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -434,8 +434,8 @@ public partial class JpegDecoderTests using Image image = provider.GetImage(JpegDecoder.Instance); JpegMetadata metadata = image.Metadata.GetJpegMetadata(); - Assert.Equal(1, metadata.Comments?.Count); - Assert.Equal(expectedComment, metadata.GetComment(0)); + Assert.Equal(1, metadata.Comments.Count); + Assert.Equal(expectedComment.ToCharArray(), metadata.Comments.ElementAtOrDefault(0)); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index bd68eaf20..8cc64acea 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -172,7 +172,7 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(1, actual.Comments.Count); - Assert.Equal("TEST COMMENT", actual.Comments.ElementAt(0).ToString()); + Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0).ToString()); } [Fact] @@ -184,8 +184,8 @@ public partial class JpegEncoderTests using var memStream = new MemoryStream(); // act - meta.SetComment(0, "First comment"); - meta.SetComment(1, "Second Comment"); + meta.Comments.Add("First comment".ToCharArray()); + meta.Comments.Add("Second Comment".ToCharArray()); input.Save(memStream, JpegEncoder); // assert @@ -193,9 +193,9 @@ public partial class JpegEncoderTests using Image output = Image.Load(memStream); JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); - Assert.Equal(2, actual.Comments?.Count); - Assert.Equal(meta.Comments?.ElementAt(0).ToString(), actual.Comments?.ElementAt(0).ToString()); - Assert.Equal(meta.Comments?.ElementAt(1).ToString(), actual.Comments?.ElementAt(1).ToString()); + Assert.Equal(2, actual.Comments.Count); + Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); } [Theory] From 980347e96fae9642cbd89bccb5a732d61865558c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Feb 2024 15:51:22 +1000 Subject: [PATCH 063/220] ENhance NormalizedFloatToByteSaturate --- src/ImageSharp/Common/Helpers/Numerics.cs | 20 +++ .../Helpers/SimdUtils.ExtendedIntrinsics.cs | 4 +- .../SimdUtils.FallbackIntrinsics128.cs | 4 +- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 150 +++++++++++------- src/ImageSharp/Common/Helpers/SimdUtils.cs | 49 +++--- .../Common/Helpers/Vector128Utilities.cs | 87 +++++++++- .../Common/Helpers/Vector256Utilities.cs | 39 ++++- .../Common/Helpers/Vector512Utilities.cs | 37 ++++- .../PixelOperations/Rgba32.PixelOperations.cs | 16 +- .../ImageSharp.Benchmarks/Bulk/FromVector4.cs | 92 ++++++----- .../Bulk/FromVector4_Rgb24.cs | 66 +++----- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 5 +- .../TestUtilities/BasicSerializer.cs | 22 +-- .../FeatureTesting/FeatureTestRunner.cs | 81 ++++++---- 14 files changed, 443 insertions(+), 229 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index ca28a7aab..5f85734e8 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -1010,6 +1010,26 @@ internal static class Numerics where TVector : struct => (uint)span.Length / (uint)Vector256.Count; + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector512Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector512.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector512Count(this ReadOnlySpan span) + where TVector : struct + => (uint)span.Length / (uint)Vector512.Count; + /// /// Gets the count of vectors that safely fit into the given span. /// diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs index ac122fc7d..7d07dcaae 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs @@ -95,7 +95,7 @@ internal static partial class SimdUtils /// internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { - VerifySpanInput(source, dest, Vector.Count); + DebugVerifySpanInput(source, dest, Vector.Count); nuint n = dest.VectorCount(); @@ -130,7 +130,7 @@ internal static partial class SimdUtils ReadOnlySpan source, Span dest) { - VerifySpanInput(source, dest, Vector.Count); + DebugVerifySpanInput(source, dest, Vector.Count); nuint n = dest.VectorCount(); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs index a551cebd0..90b313fb9 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs @@ -69,7 +69,7 @@ internal static partial class SimdUtils [MethodImpl(InliningOptions.ColdPath)] internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { - VerifySpanInput(source, dest, 4); + DebugVerifySpanInput(source, dest, 4); uint count = (uint)dest.Length / 4; if (count == 0) @@ -103,7 +103,7 @@ internal static partial class SimdUtils ReadOnlySpan source, Span dest) { - VerifySpanInput(source, dest, 4); + DebugVerifySpanInput(source, dest, 4); uint count = (uint)source.Length / 4; if (count == 0) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index f27852a82..feb55ebe5 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -17,8 +17,13 @@ internal static partial class SimdUtils { public static class HwIntrinsics { +#pragma warning disable SA1117 // Parameters should be on same line or separate lines +#pragma warning disable SA1137 // Elements should have the same indentation [MethodImpl(MethodImplOptions.AggressiveInlining)] // too much IL for JIT to inline, so give a hint - public static Vector256 PermuteMaskDeinterleave8x32() => Vector256.Create(0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsInt32(); + public static Vector256 PermuteMaskDeinterleave8x32() => Vector256.Create(0, 4, 1, 5, 2, 6, 3, 7); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 PermuteMaskDeinterleave16x32() => Vector512.Create(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 PermuteMaskEvenOdd8x32() => Vector256.Create(0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); @@ -38,17 +43,18 @@ internal static partial class SimdUtils [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 ShuffleMaskSlice4Nx16() => Vector128.Create(0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80); -#pragma warning disable SA1003, SA1116, SA1117 // Parameters should be on same line or separate lines [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 ShuffleMaskShiftAlpha() => Vector256.Create((byte) - 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15, - 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15); + private static Vector256 ShuffleMaskShiftAlpha() => Vector256.Create( + (byte)0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15, + 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 PermuteMaskShiftAlpha8x32() => Vector256.Create( - 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, - 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); -#pragma warning restore SA1003, SA1116, SA1117 // Parameters should be on same line or separate lines + public static Vector256 PermuteMaskShiftAlpha8x32() + => Vector256.Create( + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, + 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); +#pragma warning restore SA1137 // Elements should have the same indentation +#pragma warning restore SA1117 // Parameters should be on same line or separate lines /// /// Shuffle single-precision (32-bit) floating-point elements in @@ -795,7 +801,7 @@ internal static partial class SimdUtils { if (Avx2.IsSupported) { - VerifySpanInput(source, dest, Vector256.Count); + DebugVerifySpanInput(source, dest, Vector256.Count); nuint n = dest.Vector256Count(); @@ -828,7 +834,7 @@ internal static partial class SimdUtils else { // Sse - VerifySpanInput(source, dest, Vector128.Count); + DebugVerifySpanInput(source, dest, Vector128.Count); nuint n = dest.Vector128Count(); @@ -881,17 +887,24 @@ internal static partial class SimdUtils /// /// as many elements as possible, slicing them down (keeping the remainder). /// + /// The source buffer. + /// The destination buffer. [MethodImpl(InliningOptions.ShortMethod)] internal static void NormalizedFloatToByteSaturateReduce( ref ReadOnlySpan source, - ref Span dest) + ref Span destination) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - if (Avx2.IsSupported || Sse2.IsSupported) + if (Avx512BW.IsSupported || Avx2.IsSupported || Sse2.IsSupported || AdvSimd.IsSupported) { int remainder; - if (Avx2.IsSupported) + + if (Avx512BW.IsSupported) + { + remainder = Numerics.ModuloP2(source.Length, Vector512.Count); + } + else if (Avx2.IsSupported) { remainder = Numerics.ModuloP2(source.Length, Vector256.Count); } @@ -906,10 +919,10 @@ internal static partial class SimdUtils { NormalizedFloatToByteSaturate( source[..adjustedCount], - dest[..adjustedCount]); + destination[..adjustedCount]); source = source[adjustedCount..]; - dest = dest[adjustedCount..]; + destination = destination[adjustedCount..]; } } } @@ -917,25 +930,59 @@ internal static partial class SimdUtils /// /// Implementation of , which is faster on new .NET runtime. /// + /// The source buffer. + /// The destination buffer. /// /// Implementation is based on MagicScaler code: /// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L541-L622 /// internal static void NormalizedFloatToByteSaturate( ReadOnlySpan source, - Span dest) + Span destination) { - if (Avx2.IsSupported) + if (Avx512BW.IsSupported) { - VerifySpanInput(source, dest, Vector256.Count); + DebugVerifySpanInput(source, destination, Vector512.Count); + + nuint n = destination.Vector512Count(); - nuint n = dest.Vector256Count(); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector512 scale = Vector512.Create((float)byte.MaxValue); + Vector512 mask = PermuteMaskDeinterleave16x32(); - ref Vector256 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + for (nuint i = 0; i < n; i++) + { + ref Vector512 s = ref Unsafe.Add(ref sourceBase, i * 4); + + Vector512 f0 = scale * s; + Vector512 f1 = scale * Unsafe.Add(ref s, 1); + Vector512 f2 = scale * Unsafe.Add(ref s, 2); + Vector512 f3 = scale * Unsafe.Add(ref s, 3); + + Vector512 w0 = Vector512Utilities.ConvertToInt32RoundToEven(f0); + Vector512 w1 = Vector512Utilities.ConvertToInt32RoundToEven(f1); + Vector512 w2 = Vector512Utilities.ConvertToInt32RoundToEven(f2); + Vector512 w3 = Vector512Utilities.ConvertToInt32RoundToEven(f3); + + Vector512 u0 = Avx512BW.PackSignedSaturate(w0, w1); + Vector512 u1 = Avx512BW.PackSignedSaturate(w2, w3); + Vector512 b = Avx512BW.PackUnsignedSaturate(u0, u1); + b = Avx512F.PermuteVar16x32(b.AsInt32(), mask).AsByte(); + + Unsafe.Add(ref destinationBase, i) = b; + } + } + else + if (Avx2.IsSupported) + { + DebugVerifySpanInput(source, destination, Vector256.Count); + + nuint n = destination.Vector256Count(); + + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); Vector256 scale = Vector256.Create((float)byte.MaxValue); Vector256 mask = PermuteMaskDeinterleave8x32(); @@ -944,36 +991,33 @@ internal static partial class SimdUtils { ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4); - Vector256 f0 = Avx.Multiply(scale, s); - Vector256 f1 = Avx.Multiply(scale, Unsafe.Add(ref s, 1)); - Vector256 f2 = Avx.Multiply(scale, Unsafe.Add(ref s, 2)); - Vector256 f3 = Avx.Multiply(scale, Unsafe.Add(ref s, 3)); + Vector256 f0 = scale * s; + Vector256 f1 = scale * Unsafe.Add(ref s, 1); + Vector256 f2 = scale * Unsafe.Add(ref s, 2); + Vector256 f3 = scale * Unsafe.Add(ref s, 3); - Vector256 w0 = Avx.ConvertToVector256Int32(f0); - Vector256 w1 = Avx.ConvertToVector256Int32(f1); - Vector256 w2 = Avx.ConvertToVector256Int32(f2); - Vector256 w3 = Avx.ConvertToVector256Int32(f3); + Vector256 w0 = Vector256Utilities.ConvertToInt32RoundToEven(f0); + Vector256 w1 = Vector256Utilities.ConvertToInt32RoundToEven(f1); + Vector256 w2 = Vector256Utilities.ConvertToInt32RoundToEven(f2); + Vector256 w3 = Vector256Utilities.ConvertToInt32RoundToEven(f3); Vector256 u0 = Avx2.PackSignedSaturate(w0, w1); Vector256 u1 = Avx2.PackSignedSaturate(w2, w3); Vector256 b = Avx2.PackUnsignedSaturate(u0, u1); b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); - Unsafe.Add(ref destBase, i) = b; + Unsafe.Add(ref destinationBase, i) = b; } } else { - // Sse - VerifySpanInput(source, dest, Vector128.Count); - - nuint n = dest.Vector128Count(); + // Sse, AdvSimd + DebugVerifySpanInput(source, destination, Vector128.Count); - ref Vector128 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + nuint n = destination.Vector128Count(); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); Vector128 scale = Vector128.Create((float)byte.MaxValue); @@ -981,20 +1025,20 @@ internal static partial class SimdUtils { ref Vector128 s = ref Unsafe.Add(ref sourceBase, i * 4); - Vector128 f0 = Sse.Multiply(scale, s); - Vector128 f1 = Sse.Multiply(scale, Unsafe.Add(ref s, 1)); - Vector128 f2 = Sse.Multiply(scale, Unsafe.Add(ref s, 2)); - Vector128 f3 = Sse.Multiply(scale, Unsafe.Add(ref s, 3)); + Vector128 f0 = scale * s; + Vector128 f1 = scale * Unsafe.Add(ref s, 1); + Vector128 f2 = scale * Unsafe.Add(ref s, 2); + Vector128 f3 = scale * Unsafe.Add(ref s, 3); - Vector128 w0 = Sse2.ConvertToVector128Int32(f0); - Vector128 w1 = Sse2.ConvertToVector128Int32(f1); - Vector128 w2 = Sse2.ConvertToVector128Int32(f2); - Vector128 w3 = Sse2.ConvertToVector128Int32(f3); + Vector128 w0 = Vector128Utilities.ConvertToInt32RoundToEven(f0); + Vector128 w1 = Vector128Utilities.ConvertToInt32RoundToEven(f1); + Vector128 w2 = Vector128Utilities.ConvertToInt32RoundToEven(f2); + Vector128 w3 = Vector128Utilities.ConvertToInt32RoundToEven(f3); - Vector128 u0 = Sse2.PackSignedSaturate(w0, w1); - Vector128 u1 = Sse2.PackSignedSaturate(w2, w3); + Vector128 u0 = Vector128Utilities.PackSignedSaturate(w0, w1); + Vector128 u1 = Vector128Utilities.PackSignedSaturate(w2, w3); - Unsafe.Add(ref destBase, i) = Sse2.PackUnsignedSaturate(u0, u1); + Unsafe.Add(ref destinationBase, i) = Vector128Utilities.PackUnsignedSaturate(u0, u1); } } } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 497e3cc6a..002c1f8da 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -94,35 +94,31 @@ internal static partial class SimdUtils } /// - /// Convert all values normalized into [0..1] from 'source' into 'dest' buffer of . + /// Convert all values normalized into [0..1] from 'source' into 'destination' buffer of . /// The values are scaled up into [0-255] and rounded, overflows are clamped. - /// should be the of the same size as , + /// should be the of the same size as , /// but there are no restrictions on the span's length. /// /// The source span of floats - /// The destination span of bytes + /// The destination span of bytes [MethodImpl(InliningOptions.ShortMethod)] - internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) + internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span destination) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref dest); - - // Also deals with the remainder from previous conversions: - FallbackIntrinsics128.NormalizedFloatToByteSaturateReduce(ref source, ref dest); + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); + HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination); // Deal with the remainder: if (source.Length > 0) { - ConvertNormalizedFloatToByteRemainder(source, dest); + ConvertNormalizedFloatToByteRemainder(source, destination); } } [MethodImpl(InliningOptions.ColdPath)] - private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span dest) + private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span destination) { ref byte sBase = ref MemoryMarshal.GetReference(source); - ref float dBase = ref MemoryMarshal.GetReference(dest); + ref float dBase = ref MemoryMarshal.GetReference(destination); // There are at most 3 elements at this point, having a for loop is overkill. // Let's minimize the no. of instructions! @@ -140,23 +136,14 @@ internal static partial class SimdUtils } } - [MethodImpl(InliningOptions.ColdPath)] - private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span dest) + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span destination) { ref float sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(dest); - - switch (source.Length) + ref byte dBase = ref MemoryMarshal.GetReference(destination); + for (int i = 0; i < source.Length; i++) { - case 3: - Unsafe.Add(ref dBase, 2) = ConvertToByte(Unsafe.Add(ref sBase, 2)); - goto case 2; - case 2: - Unsafe.Add(ref dBase, 1) = ConvertToByte(Unsafe.Add(ref sBase, 1)); - goto case 1; - case 1: - dBase = ConvertToByte(sBase); - break; + Unsafe.Add(ref dBase, i) = ConvertToByte(Unsafe.Add(ref sBase, i)); } } @@ -173,7 +160,7 @@ internal static partial class SimdUtils } [Conditional("DEBUG")] - private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) + private static void DebugVerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) { DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); DebugGuard.IsTrue( @@ -183,11 +170,11 @@ internal static partial class SimdUtils } [Conditional("DEBUG")] - private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) + private static void DebugVerifySpanInput(ReadOnlySpan source, Span destination, int shouldBeDivisibleBy) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); DebugGuard.IsTrue( - Numerics.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0, + Numerics.ModuloP2(destination.Length, shouldBeDivisibleBy) == 0, nameof(source), $"length should be divisible by {shouldBeDivisibleBy}!"); } diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 981f9a47f..a07fa8ca6 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -26,7 +26,7 @@ internal static class Vector128Utilities public static bool SupportsShuffleFloat { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Sse.IsSupported; + get => Sse.IsSupported || AdvSimd.IsSupported; } /// @@ -62,6 +62,7 @@ internal static class Vector128Utilities /// The input vector from which values are selected. /// The shuffle control byte. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Shuffle(Vector128 vector, [ConstantExpected] byte control) { if (Sse.IsSupported) @@ -69,6 +70,17 @@ internal static class Vector128Utilities return Sse.Shuffle(vector, vector, control); } + if (AdvSimd.IsSupported) + { +#pragma warning disable CA1857 // A constant is expected for the parameter + Vector128 result = Vector128.Create(AdvSimd.Extract(vector, (byte)(control & 0x3))); + result = AdvSimd.Insert(result, 1, AdvSimd.Extract(vector, (byte)((control >> 2) & 0x3))); + result = AdvSimd.Insert(result, 2, AdvSimd.Extract(vector, (byte)((control >> 4) & 0x3))); + result = AdvSimd.Insert(result, 3, AdvSimd.Extract(vector, (byte)((control >> 6) & 0x3))); +#pragma warning restore CA1857 // A constant is expected for the parameter + return result; + } + ThrowUnreachableException(); return default; } @@ -84,6 +96,7 @@ internal static class Vector128Utilities /// /// A new vector containing the values from selected by the given . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Shuffle(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) @@ -155,6 +168,7 @@ internal static class Vector128Utilities /// The right hand source vector. /// An 8-bit mask used for the operation. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AlignRight(Vector128 left, Vector128 right, [ConstantExpected(Max = (byte)15)] byte mask) { if (Ssse3.IsSupported) @@ -171,6 +185,77 @@ internal static class Vector128Utilities return default; } + /// + /// Performs a conversion from a 128-bit vector of 4 single-precision floating-point values to a 128-bit vector of 4 signed 32-bit integer values. + /// Rounding is equivalent to . + /// + /// The value to convert. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 ConvertToInt32RoundToEven(Vector128 vector) + { + if (Sse2.IsSupported) + { + return Sse2.ConvertToVector128Int32(vector); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ConvertToInt32RoundToEven(vector); + } + + Vector128 sign = vector & Vector128.Create(-0.0f); + Vector128 val_2p23_f32 = sign | Vector128.Create(8388608.0f); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return Vector128.ConvertToInt32(val_2p23_f32 | sign); + } + + /// + /// Packs signed 16-bit integers to unsigned 8-bit integers and saturates. + /// + /// The left hand source vector. + /// The right hand source vector. + /// The . + public static Vector128 PackUnsignedSaturate(Vector128 left, Vector128 right) + { + if (Sse2.IsSupported) + { + return Sse2.PackUnsignedSaturate(left, right); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ExtractNarrowingSaturateUnsignedUpper(AdvSimd.ExtractNarrowingSaturateUnsignedLower(left), right); + } + + ThrowUnreachableException(); + return default; + } + + /// + /// Packs signed 32-bit integers to signed 16-bit integers and saturates. + /// + /// The left hand source vector. + /// The right hand source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 PackSignedSaturate(Vector128 left, Vector128 right) + { + if (Sse2.IsSupported) + { + return Sse2.PackSignedSaturate(left, right); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ExtractNarrowingSaturateUpper(AdvSimd.ExtractNarrowingSaturateLower(left), right); + } + + ThrowUnreachableException(); + return default; + } + [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); } diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs index 14fa24b31..6e8c0d1de 100644 --- a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -25,7 +25,7 @@ internal static class Vector256Utilities public static bool SupportsShuffleFloat { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Avx.IsSupported; + get => Avx.IsSupported || Sse.IsSupported; } /// @@ -43,6 +43,7 @@ internal static class Vector256Utilities /// The input vector from which values are selected. /// The shuffle control byte. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Shuffle(Vector256 vector, [ConstantExpected] byte control) { if (Avx.IsSupported) @@ -50,6 +51,13 @@ internal static class Vector256Utilities return Avx.Shuffle(vector, vector, control); } + if (Sse.IsSupported) + { + Vector128 lower = vector.GetLower(); + Vector128 upper = vector.GetUpper(); + return Vector256.Create(Sse.Shuffle(lower, lower, control), Sse.Shuffle(upper, upper, control)); + } + ThrowUnreachableException(); return default; } @@ -62,6 +70,7 @@ internal static class Vector256Utilities /// The per-element indices used to select a value from . /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Shuffle(Vector256 vector, Vector256 indices) { if (Avx2.IsSupported) @@ -73,6 +82,34 @@ internal static class Vector256Utilities return default; } + /// + /// Performs a conversion from a 256-bit vector of 8 single-precision floating-point values to a 256-bit vector of 8 signed 32-bit integer values. + /// Rounding is equivalent to . + /// + /// The value to convert. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ConvertToInt32RoundToEven(Vector256 vector) + { + if (Avx.IsSupported) + { + return Avx.ConvertToVector256Int32(vector); + } + + if (Sse2.IsSupported) + { + Vector128 lower = Sse2.ConvertToVector128Int32(vector.GetLower()); + Vector128 upper = Sse2.ConvertToVector128Int32(vector.GetUpper()); + return Vector256.Create(lower, upper); + } + + Vector256 sign = vector & Vector256.Create(-0.0f); + Vector256 val_2p23_f32 = sign | Vector256.Create(8388608.0f); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return Vector256.ConvertToInt32(val_2p23_f32 | sign); + } + [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); } diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs index 5488b4064..0165af90e 100644 --- a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -25,7 +25,7 @@ internal static class Vector512Utilities public static bool SupportsShuffleFloat { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Avx512F.IsSupported; + get => Avx512F.IsSupported || Avx.IsSupported; } /// @@ -51,6 +51,13 @@ internal static class Vector512Utilities return Avx512F.Shuffle(vector, vector, control); } + if (Avx.IsSupported) + { + Vector256 lower = vector.GetLower(); + Vector256 upper = vector.GetUpper(); + return Vector512.Create(Avx.Shuffle(lower, lower, control), Avx.Shuffle(upper, upper, control)); + } + ThrowUnreachableException(); return default; } @@ -75,6 +82,34 @@ internal static class Vector512Utilities return default; } + /// + /// Performs a conversion from a 512-bit vector of 16 single-precision floating-point values to a 512-bit vector of 16 signed 32-bit integer values. + /// Rounding is equivalent to . + /// + /// The value to convert. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ConvertToInt32RoundToEven(Vector512 vector) + { + if (Avx512F.IsSupported) + { + return Avx512F.ConvertToVector512Int32(vector); + } + + if (Avx.IsSupported) + { + Vector256 lower = Avx.ConvertToVector256Int32(vector.GetLower()); + Vector256 upper = Avx.ConvertToVector256Int32(vector.GetUpper()); + return Vector512.Create(lower, upper); + } + + Vector512 sign = vector & Vector512.Create(-0.0f); + Vector512 val_2p23_f32 = sign | Vector512.Create(8388608.0f); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return Vector512.ConvertToInt32(val_2p23_f32 | sign); + } + [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs index ed89585dc..065e34c33 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs @@ -20,15 +20,15 @@ public partial struct Rgba32 /// public override void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, + ReadOnlySpan source, Span destinationVectors, PixelConversionModifiers modifiers) { - Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, nameof(destinationVectors)); + Guard.DestinationShouldNotBeTooShort(source, destinationVectors, nameof(destinationVectors)); - destinationVectors = destinationVectors[..sourcePixels.Length]; + destinationVectors = destinationVectors[..source.Length]; SimdUtils.ByteToNormalizedFloat( - MemoryMarshal.Cast(sourcePixels), + MemoryMarshal.Cast(source), MemoryMarshal.Cast(destinationVectors)); Vector4Converters.ApplyForwardConversionModifiers(destinationVectors, modifiers); } @@ -37,16 +37,16 @@ public partial struct Rgba32 public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { - Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, nameof(destinationPixels)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); - destinationPixels = destinationPixels[..sourceVectors.Length]; + destination = destination[..sourceVectors.Length]; Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); SimdUtils.NormalizedFloatToByteSaturate( MemoryMarshal.Cast(sourceVectors), - MemoryMarshal.Cast(destinationPixels)); + MemoryMarshal.Cast(destination)); } /// diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index ecd16b957..dff687fa1 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -18,9 +18,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class FromVector4 where TPixel : unmanaged, IPixel { - protected IMemoryOwner source; + protected IMemoryOwner Source { get; set; } - protected IMemoryOwner destination; + protected IMemoryOwner Destination { get; set; } protected Configuration Configuration => Configuration.Default; @@ -31,22 +31,22 @@ public abstract class FromVector4 [GlobalSetup] public void Setup() { - this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); - this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.Destination = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.Source = this.Configuration.MemoryAllocator.Allocate(this.Count); } [GlobalCleanup] public void Cleanup() { - this.destination.Dispose(); - this.source.Dispose(); + this.Destination.Dispose(); + this.Source.Dispose(); } // [Benchmark] public void PerElement() { - ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); - ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); + ref Vector4 s = ref MemoryMarshal.GetReference(this.Source.GetSpan()); + ref TPixel d = ref MemoryMarshal.GetReference(this.Destination.GetSpan()); for (nuint i = 0; i < (uint)this.Count; i++) { Unsafe.Add(ref d, i) = TPixel.FromVector4(Unsafe.Add(ref s, i)); @@ -55,11 +55,11 @@ public abstract class FromVector4 [Benchmark(Baseline = true)] public void PixelOperations_Base() - => new PixelOperations().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + => new PixelOperations().FromVector4Destructive(this.Configuration, this.Source.GetSpan(), this.Destination.GetSpan()); [Benchmark] public void PixelOperations_Specialized() - => PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + => PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.Source.GetSpan(), this.Destination.GetSpan()); } public class FromVector4Rgba32 : FromVector4 @@ -67,8 +67,8 @@ public class FromVector4Rgba32 : FromVector4 [Benchmark] public void FallbackIntrinsics128() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats); } @@ -76,8 +76,8 @@ public class FromVector4Rgba32 : FromVector4 [Benchmark] public void ExtendedIntrinsic() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); } @@ -85,8 +85,8 @@ public class FromVector4Rgba32 : FromVector4 [Benchmark] public void UseHwIntrinsics() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); } @@ -96,8 +96,8 @@ public class FromVector4Rgba32 : FromVector4 [Benchmark] public void UseAvx2_Grouped() { - Span src = MemoryMarshal.Cast(this.source.GetSpan()); - Span dest = MemoryMarshal.Cast(this.destination.GetSpan()); + Span src = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dest = MemoryMarshal.Cast(this.Destination.GetSpan()); nuint n = (uint)dest.Length / (uint)Vector.Count; @@ -107,7 +107,7 @@ public class FromVector4Rgba32 : FromVector4 ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); Vector256 mask = Unsafe.As>(ref maskBase); - var maxBytes = Vector256.Create(255f); + Vector256 maxBytes = Vector256.Create(255f); for (nuint i = 0; i < n; i++) { @@ -137,25 +137,37 @@ public class FromVector4Rgba32 : FromVector4 } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 ConvertToInt32(Vector256 vf, Vector256 scale) - { - vf = Avx.Multiply(scale, vf); - return Avx.ConvertToVector256Int32(vf); - } - - // *** RESULTS 2020 March: *** - // Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores - // .NET Core SDK=3.1.200-preview-014971 - // Job-IUZXZT : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT - // - // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |---------------------------- |------ |-----------:|------------:|----------:|------:|--------:|------:|------:|------:|----------:| - // | FallbackIntrinsics128 | 1024 | 2,952.6 ns | 1,680.77 ns | 92.13 ns | 3.32 | 0.16 | - | - | - | - | - // | BasicIntrinsics256 | 1024 | 1,664.5 ns | 928.11 ns | 50.87 ns | 1.87 | 0.09 | - | - | - | - | - // | ExtendedIntrinsic | 1024 | 890.6 ns | 375.48 ns | 20.58 ns | 1.00 | 0.00 | - | - | - | - | - // | UseAvx2 | 1024 | 299.0 ns | 30.47 ns | 1.67 ns | 0.34 | 0.01 | - | - | - | - | - // | UseAvx2_Grouped | 1024 | 318.1 ns | 48.19 ns | 2.64 ns | 0.36 | 0.01 | - | - | - | - | - // | PixelOperations_Base | 1024 | 8,136.9 ns | 1,834.82 ns | 100.57 ns | 9.14 | 0.26 | - | - | - | 24 B | - // | PixelOperations_Specialized | 1024 | 951.1 ns | 123.93 ns | 6.79 ns | 1.07 | 0.03 | - | - | - | - | + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3085/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + Job-YJYLLR : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + Runtime=.NET 8.0 Arguments=/p:DebugType=portable IterationCount=3 + LaunchCount=1 WarmupCount=3 + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio | + |---------------------------- |------ |------------:|-------------:|-----------:|------:|--------:|----------:|------------:| + | PixelOperations_Base | 64 | 114.80 ns | 16.459 ns | 0.902 ns | 1.00 | 0.00 | - | NA | + | PixelOperations_Specialized | 64 | 28.91 ns | 80.482 ns | 4.411 ns | 0.25 | 0.04 | - | NA | + | FallbackIntrinsics128 | 64 | 133.60 ns | 23.750 ns | 1.302 ns | 1.16 | 0.02 | - | NA | + | ExtendedIntrinsic | 64 | 40.11 ns | 10.183 ns | 0.558 ns | 0.35 | 0.01 | - | NA | + | UseHwIntrinsics | 64 | 14.71 ns | 4.860 ns | 0.266 ns | 0.13 | 0.00 | - | NA | + | UseAvx2_Grouped | 64 | 20.23 ns | 11.619 ns | 0.637 ns | 0.18 | 0.00 | - | NA | + | | | | | | | | | | + | PixelOperations_Base | 256 | 387.94 ns | 31.591 ns | 1.732 ns | 1.00 | 0.00 | - | NA | + | PixelOperations_Specialized | 256 | 50.93 ns | 22.388 ns | 1.227 ns | 0.13 | 0.00 | - | NA | + | FallbackIntrinsics128 | 256 | 509.72 ns | 249.926 ns | 13.699 ns | 1.31 | 0.04 | - | NA | + | ExtendedIntrinsic | 256 | 140.32 ns | 9.353 ns | 0.513 ns | 0.36 | 0.00 | - | NA | + | UseHwIntrinsics | 256 | 41.99 ns | 16.000 ns | 0.877 ns | 0.11 | 0.00 | - | NA | + | UseAvx2_Grouped | 256 | 63.81 ns | 2.360 ns | 0.129 ns | 0.16 | 0.00 | - | NA | + | | | | | | | | | | + | PixelOperations_Base | 2048 | 2,979.49 ns | 2,023.706 ns | 110.926 ns | 1.00 | 0.00 | - | NA | + | PixelOperations_Specialized | 2048 | 326.19 ns | 19.077 ns | 1.046 ns | 0.11 | 0.00 | - | NA | + | FallbackIntrinsics128 | 2048 | 3,885.95 ns | 411.078 ns | 22.533 ns | 1.31 | 0.05 | - | NA | + | ExtendedIntrinsic | 2048 | 1,078.58 ns | 136.960 ns | 7.507 ns | 0.36 | 0.01 | - | NA | + | UseHwIntrinsics | 2048 | 312.07 ns | 68.662 ns | 3.764 ns | 0.10 | 0.00 | - | NA | + | UseAvx2_Grouped | 2048 | 451.83 ns | 41.742 ns | 2.288 ns | 0.15 | 0.01 | - | NA | + */ } diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs index 27fab64dd..c6125ef8f 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs @@ -7,48 +7,26 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.Short))] -public class FromVector4_Rgb24 : FromVector4 -{ -} +public class FromVector4_Rgb24 : FromVector4; -// 2020-11-02 -// ########## -// -// BenchmarkDotNet = v0.12.1, OS = Windows 10.0.19041.572(2004 /?/ 20H1) -// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores -// .NET Core SDK=3.1.403 -// [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT -// Job-XYEQXL : .NET Framework 4.8 (4.8.4250.0), X64 RyuJIT -// Job-HSXNJV : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT -// Job-YUREJO : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT -// -// IterationCount=3 LaunchCount=1 WarmupCount=3 -// -// | Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |---------------------------- |----------- |-------------- |------ |-----------:|------------:|----------:|------:|--------:|-------:|------:|------:|----------:| -// | PixelOperations_Base | Job-XYEQXL | .NET 4.7.2 | 64 | 343.2 ns | 305.91 ns | 16.77 ns | 1.00 | 0.00 | 0.0057 | - | - | 24 B | -// | PixelOperations_Specialized | Job-XYEQXL | .NET 4.7.2 | 64 | 320.8 ns | 19.93 ns | 1.09 ns | 0.94 | 0.05 | - | - | - | - | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-HSXNJV | .NET Core 2.1 | 64 | 234.3 ns | 17.98 ns | 0.99 ns | 1.00 | 0.00 | 0.0052 | - | - | 24 B | -// | PixelOperations_Specialized | Job-HSXNJV | .NET Core 2.1 | 64 | 246.0 ns | 82.34 ns | 4.51 ns | 1.05 | 0.02 | - | - | - | - | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-YUREJO | .NET Core 3.1 | 64 | 222.3 ns | 39.46 ns | 2.16 ns | 1.00 | 0.00 | 0.0057 | - | - | 24 B | -// | PixelOperations_Specialized | Job-YUREJO | .NET Core 3.1 | 64 | 243.4 ns | 33.58 ns | 1.84 ns | 1.09 | 0.01 | - | - | - | - | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-XYEQXL | .NET 4.7.2 | 256 | 824.9 ns | 32.77 ns | 1.80 ns | 1.00 | 0.00 | 0.0057 | - | - | 24 B | -// | PixelOperations_Specialized | Job-XYEQXL | .NET 4.7.2 | 256 | 967.0 ns | 39.09 ns | 2.14 ns | 1.17 | 0.01 | 0.0172 | - | - | 72 B | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-HSXNJV | .NET Core 2.1 | 256 | 756.9 ns | 94.43 ns | 5.18 ns | 1.00 | 0.00 | 0.0048 | - | - | 24 B | -// | PixelOperations_Specialized | Job-HSXNJV | .NET Core 2.1 | 256 | 1,003.3 ns | 3,192.09 ns | 174.97 ns | 1.32 | 0.22 | 0.0172 | - | - | 72 B | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-YUREJO | .NET Core 3.1 | 256 | 748.6 ns | 248.03 ns | 13.60 ns | 1.00 | 0.00 | 0.0057 | - | - | 24 B | -// | PixelOperations_Specialized | Job-YUREJO | .NET Core 3.1 | 256 | 437.0 ns | 36.48 ns | 2.00 ns | 0.58 | 0.01 | 0.0172 | - | - | 72 B | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-XYEQXL | .NET 4.7.2 | 2048 | 5,751.6 ns | 704.24 ns | 38.60 ns | 1.00 | 0.00 | - | - | - | 24 B | -// | PixelOperations_Specialized | Job-XYEQXL | .NET 4.7.2 | 2048 | 4,391.6 ns | 718.17 ns | 39.37 ns | 0.76 | 0.00 | 0.0153 | - | - | 72 B | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-HSXNJV | .NET Core 2.1 | 2048 | 6,202.0 ns | 1,815.18 ns | 99.50 ns | 1.00 | 0.00 | - | - | - | 24 B | -// | PixelOperations_Specialized | Job-HSXNJV | .NET Core 2.1 | 2048 | 4,225.6 ns | 1,004.03 ns | 55.03 ns | 0.68 | 0.01 | 0.0153 | - | - | 72 B | -// | | | | | | | | | | | | | | -// | PixelOperations_Base | Job-YUREJO | .NET Core 3.1 | 2048 | 6,157.1 ns | 2,516.98 ns | 137.96 ns | 1.00 | 0.00 | - | - | - | 24 B | -// | PixelOperations_Specialized | Job-YUREJO | .NET Core 3.1 | 2048 | 1,822.7 ns | 1,764.43 ns | 96.71 ns | 0.30 | 0.02 | 0.0172 | - | - | 72 B | +/* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3085/23H2/2023Update/SunValley3) +11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +.NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + Job-NEHCEM : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + +Runtime=.NET 8.0 Arguments=/p:DebugType=portable IterationCount=3 +LaunchCount=1 WarmupCount=3 + +| Method | Count | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | +|---------------------------- |------ |------------:|----------:|---------:|------:|-------:|----------:|------------:| +| PixelOperations_Base | 64 | 95.87 ns | 13.60 ns | 0.745 ns | 1.00 | - | - | NA | +| PixelOperations_Specialized | 64 | 97.34 ns | 30.34 ns | 1.663 ns | 1.02 | - | - | NA | +| | | | | | | | | | +| PixelOperations_Base | 256 | 337.80 ns | 88.10 ns | 4.829 ns | 1.00 | - | - | NA | +| PixelOperations_Specialized | 256 | 195.07 ns | 30.54 ns | 1.674 ns | 0.58 | 0.0153 | 96 B | NA | +| | | | | | | | | | +| PixelOperations_Base | 2048 | 2,561.79 ns | 162.45 ns | 8.905 ns | 1.00 | - | - | NA | +| PixelOperations_Specialized | 2048 | 741.85 ns | 18.05 ns | 0.989 ns | 0.29 | 0.0153 | 96 B | NA | + */ diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index c81eaea63..200371679 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -112,6 +112,7 @@ public partial class SimdUtilsTests public static readonly TheoryData ArraySizesDivisibleBy4 = new() { 0, 4, 8, 28, 1020 }; public static readonly TheoryData ArraySizesDivisibleBy3 = new() { 0, 3, 9, 36, 957 }; public static readonly TheoryData ArraySizesDivisibleBy32 = new() { 0, 32, 512 }; + public static readonly TheoryData ArraySizesDivisibleBy64 = new() { 0, 64, 512 }; public static readonly TheoryData ArbitraryArraySizes = new() { 0, 1, 2, 3, 4, 7, 8, 9, 15, 16, 17, 63, 64, 255, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520 }; @@ -199,7 +200,7 @@ public partial class SimdUtilsTests } [Theory] - [MemberData(nameof(ArraySizesDivisibleBy32))] + [MemberData(nameof(ArraySizesDivisibleBy64))] public void HwIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) { if (!Sse2.IsSupported) @@ -214,7 +215,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512BW | HwIntrinsics.DisableAVX2); } [Theory] diff --git a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs index d161b8019..216ed95b8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs @@ -13,14 +13,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; /// internal class BasicSerializer : IXunitSerializationInfo { - private readonly Dictionary map = new Dictionary(); + private readonly Dictionary map = []; public const char Separator = ':'; private string DumpToString(Type type) { - using var ms = new MemoryStream(); - using var writer = new StreamWriter(ms); + using MemoryStream ms = new(); + using StreamWriter writer = new(ms); writer.WriteLine(type.FullName); foreach (KeyValuePair kv in this.map) { @@ -29,16 +29,16 @@ internal class BasicSerializer : IXunitSerializationInfo writer.Flush(); byte[] data = ms.ToArray(); - return System.Convert.ToBase64String(data); + return Convert.ToBase64String(data); } private Type LoadDump(string dump) { - byte[] data = System.Convert.FromBase64String(dump); + byte[] data = Convert.FromBase64String(dump); - using var ms = new MemoryStream(data); - using var reader = new StreamReader(ms); - var type = Type.GetType(reader.ReadLine()); + using MemoryStream ms = new(data); + using StreamReader reader = new(ms); + Type type = Type.GetType(reader.ReadLine()); for (string s = reader.ReadLine(); s != null; s = reader.ReadLine()) { string[] kv = s.Split(Separator); @@ -50,7 +50,7 @@ internal class BasicSerializer : IXunitSerializationInfo public static string Serialize(IXunitSerializable serializable) { - var serializer = new BasicSerializer(); + BasicSerializer serializer = new(); serializable.Serialize(serializer); return serializer.DumpToString(serializable.GetType()); } @@ -58,10 +58,10 @@ internal class BasicSerializer : IXunitSerializationInfo public static T Deserialize(string dump) where T : IXunitSerializable { - var serializer = new BasicSerializer(); + BasicSerializer serializer = new(); Type type = serializer.LoadDump(dump); - var result = (T)Activator.CreateInstance(type); + T result = (T)Activator.CreateInstance(type); result.Deserialize(serializer); return result; } diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index 5a9a72f96..07ad5e8f0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Diagnostics; +using System.Globalization; using Microsoft.DotNet.RemoteExecutor; using Xunit.Abstractions; @@ -12,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; /// public static class FeatureTestRunner { - private static readonly char[] SplitChars = { ',', ' ' }; + private static readonly char[] SplitChars = [',', ' ']; /// /// Allows the deserialization of parameters passed to the feature test. @@ -40,7 +41,7 @@ public static class FeatureTestRunner /// The value. public static T Deserialize(string value) where T : IConvertible - => (T)Convert.ChangeType(value, typeof(T)); + => (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); /// /// Runs the given test within an environment @@ -127,6 +128,7 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. /// The test action to run. /// The intrinsics features. /// The value to pass as a parameter to the test action. @@ -170,6 +172,7 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. /// The test action to run. /// The intrinsics features. /// The value to pass as a parameter to the test action. @@ -214,6 +217,8 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. + /// The addition type of argument. /// The test action to run. /// The intrinsics features. /// The value to pass as a parameter to the test action. @@ -261,6 +266,7 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. /// The test action to run. /// The intrinsics features. /// The value to pass as a parameter to the test action. @@ -307,6 +313,7 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. /// The test action to run. /// The value to pass as a parameter to the test action. /// The intrinsics features. @@ -350,6 +357,7 @@ public static class FeatureTestRunner /// Runs the given test within an environment /// where the given features. /// + /// The type of argument. /// The test action to run. /// The value to pass as a parameter #0 to the test action. /// The value to pass as a parameter #1 to the test action. @@ -395,10 +403,10 @@ public static class FeatureTestRunner internal static Dictionary ToFeatureKeyValueCollection(this HwIntrinsics intrinsics) { // Loop through and translate the given values into COMPlus equivalents - Dictionary features = new(); + Dictionary features = []; foreach (string intrinsic in intrinsics.ToString("G").Split(SplitChars, StringSplitOptions.RemoveEmptyEntries)) { - HwIntrinsics key = (HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic); + HwIntrinsics key = Enum.Parse(intrinsic); switch (intrinsic) { case nameof(HwIntrinsics.AllowAll): @@ -418,40 +426,47 @@ public static class FeatureTestRunner } /// -/// See -/// -/// ends up impacting all SIMD support(including System.Numerics) -/// but not things like , , and . -/// +/// See /// [Flags] #pragma warning disable RCS1135 // Declare enum member with zero value (when enum has FlagsAttribute). -public enum HwIntrinsics +public enum HwIntrinsics : long #pragma warning restore RCS1135 // Declare enum member with zero value (when enum has FlagsAttribute). { // Use flags so we can pass multiple values without using params. // Don't base on 0 or use inverse for All as that doesn't translate to string values. - DisableHWIntrinsic = 1 << 0, - DisableSSE = 1 << 1, - DisableSSE2 = 1 << 2, - DisableAES = 1 << 3, - DisablePCLMULQDQ = 1 << 4, - DisableSSE3 = 1 << 5, - DisableSSSE3 = 1 << 6, - DisableSSE41 = 1 << 7, - DisableSSE42 = 1 << 8, - DisablePOPCNT = 1 << 9, - DisableAVX = 1 << 10, - DisableFMA = 1 << 11, - DisableAVX2 = 1 << 12, - DisableBMI1 = 1 << 13, - DisableBMI2 = 1 << 14, - DisableLZCNT = 1 << 15, - DisableArm64AdvSimd = 1 << 16, - DisableArm64Crc32 = 1 << 17, - DisableArm64Dp = 1 << 18, - DisableArm64Aes = 1 << 19, - DisableArm64Sha1 = 1 << 20, - DisableArm64Sha256 = 1 << 21, - AllowAll = 1 << 22 + DisableHWIntrinsic = 1L << 0, + DisableSSE = 1L << 1, + DisableSSE2 = 1L << 2, + DisableAES = 1L << 3, + DisablePCLMULQDQ = 1L << 4, + DisableSSE3 = 1L << 5, + DisableSSSE3 = 1L << 6, + DisableSSE41 = 1L << 7, + DisableSSE42 = 1L << 8, + DisablePOPCNT = 1L << 9, + DisableAVX = 1L << 10, + DisableFMA = 1L << 11, + DisableAVX2 = 1L << 12, + DisableAVXVNNI = 1L << 13, + DisableAVX512BW = 1L << 14, + DisableAVX512BW_VL = 1L << 15, + DisableAVX512CD = 1L << 16, + DisableAVX512CD_VL = 1L << 17, + DisableAVX512DQ = 1L << 18, + DisableAVX512DQ_VL = 1L << 19, + DisableAVX512F = 1L << 20, + DisableAVX512F_VL = 1L << 21, + DisableAVX512VBMI = 1L << 22, + DisableAVX512VBMI_VL = 1L << 23, + DisableBMI1 = 1L << 24, + DisableBMI2 = 1L << 25, + DisableLZCNT = 1L << 26, + DisableArm64AdvSimd = 1L << 27, + DisableArm64Crc32 = 1L << 28, + DisableArm64Dp = 1L << 29, + DisableArm64Aes = 1L << 30, + DisableArm64Sha1 = 1L << 31, + DisableArm64Sha256 = 1L << 32, + AllowAll = 1L << 33 } From 89cd8492f1aef9b233f3b6b203efa4083cc04215 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Feb 2024 18:59:27 +1000 Subject: [PATCH 064/220] Remove unused methods --- .../Helpers/SimdUtils.ExtendedIntrinsics.cs | 144 ------------------ .../SimdUtils.FallbackIntrinsics128.cs | 61 -------- .../ImageSharp.Benchmarks/Bulk/FromVector4.cs | 18 --- .../Bulk/ToVector4_Rgba32.cs | 9 -- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 18 --- 5 files changed, 250 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs index 7d07dcaae..3c2f189cf 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; // ReSharper disable MemberHidesStaticFromOuterClass namespace SixLabors.ImageSharp; @@ -35,148 +34,5 @@ internal static partial class SimdUtils dest1 = Vector.ConvertToSingle(i1); dest2 = Vector.ConvertToSingle(i2); } - - /// - /// as many elements as possible, slicing them down (keeping the remainder). - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void ByteToNormalizedFloatReduce( - ref ReadOnlySpan source, - ref Span dest) - { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - if (!IsAvailable) - { - return; - } - - int remainder = Numerics.ModuloP2(source.Length, Vector.Count); - int adjustedCount = source.Length - remainder; - - if (adjustedCount > 0) - { - ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]); - - source = source[adjustedCount..]; - dest = dest[adjustedCount..]; - } - } - - /// - /// as many elements as possible, slicing them down (keeping the remainder). - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void NormalizedFloatToByteSaturateReduce( - ref ReadOnlySpan source, - ref Span dest) - { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - if (!IsAvailable) - { - return; - } - - int remainder = Numerics.ModuloP2(source.Length, Vector.Count); - int adjustedCount = source.Length - remainder; - - if (adjustedCount > 0) - { - NormalizedFloatToByteSaturate(source[..adjustedCount], dest[..adjustedCount]); - - source = source[adjustedCount..]; - dest = dest[adjustedCount..]; - } - } - - /// - /// Implementation , which is faster on new RyuJIT runtime. - /// - internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) - { - DebugVerifySpanInput(source, dest, Vector.Count); - - nuint n = dest.VectorCount(); - - ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - - for (nuint i = 0; i < n; i++) - { - Vector b = Unsafe.Add(ref sourceBase, i); - - Vector.Widen(b, out Vector s0, out Vector s1); - Vector.Widen(s0, out Vector w0, out Vector w1); - Vector.Widen(s1, out Vector w2, out Vector w3); - - Vector f0 = ConvertToSingle(w0); - Vector f1 = ConvertToSingle(w1); - Vector f2 = ConvertToSingle(w2); - Vector f3 = ConvertToSingle(w3); - - ref Vector d = ref Unsafe.Add(ref destBase, i * 4); - d = f0; - Unsafe.Add(ref d, 1) = f1; - Unsafe.Add(ref d, 2) = f2; - Unsafe.Add(ref d, 3) = f3; - } - } - - /// - /// Implementation of , which is faster on new .NET runtime. - /// - internal static void NormalizedFloatToByteSaturate( - ReadOnlySpan source, - Span dest) - { - DebugVerifySpanInput(source, dest, Vector.Count); - - nuint n = dest.VectorCount(); - - ref Vector sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - - for (nuint i = 0; i < n; i++) - { - ref Vector s = ref Unsafe.Add(ref sourceBase, i * 4); - - Vector f0 = s; - Vector f1 = Unsafe.Add(ref s, 1); - Vector f2 = Unsafe.Add(ref s, 2); - Vector f3 = Unsafe.Add(ref s, 3); - - Vector w0 = ConvertToUInt32(f0); - Vector w1 = ConvertToUInt32(f1); - Vector w2 = ConvertToUInt32(f2); - Vector w3 = ConvertToUInt32(f3); - - var u0 = Vector.Narrow(w0, w1); - var u1 = Vector.Narrow(w2, w3); - - Unsafe.Add(ref destBase, i) = Vector.Narrow(u0, u1); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector ConvertToUInt32(Vector vf) - { - var maxBytes = new Vector(255f); - vf *= maxBytes; - vf += new Vector(0.5f); - vf = Vector.Min(Vector.Max(vf, Vector.Zero), maxBytes); - var vi = Vector.ConvertToInt32(vf); - return Vector.AsVectorUInt32(vi); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector ConvertToSingle(Vector u) - { - var vi = Vector.AsVectorInt32(u); - var v = Vector.ConvertToSingle(vi); - v *= new Vector(1f / 255f); - return v; - } } } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs index 90b313fb9..fcf441c47 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs @@ -39,30 +39,6 @@ internal static partial class SimdUtils } } - /// - /// as many elements as possible, slicing them down (keeping the remainder). - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void NormalizedFloatToByteSaturateReduce( - ref ReadOnlySpan source, - ref Span dest) - { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - int remainder = Numerics.Modulo4(source.Length); - int adjustedCount = source.Length - remainder; - - if (adjustedCount > 0) - { - NormalizedFloatToByteSaturate( - source[..adjustedCount], - dest[..adjustedCount]); - - source = source[adjustedCount..]; - dest = dest[adjustedCount..]; - } - } - /// /// Implementation of using . /// @@ -95,43 +71,6 @@ internal static partial class SimdUtils } } - /// - /// Implementation of using . - /// - [MethodImpl(InliningOptions.ColdPath)] - internal static void NormalizedFloatToByteSaturate( - ReadOnlySpan source, - Span dest) - { - DebugVerifySpanInput(source, dest, 4); - - uint count = (uint)source.Length / 4; - if (count == 0) - { - return; - } - - ref Vector4 sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref ByteVector4 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - - var half = new Vector4(0.5f); - var maxBytes = new Vector4(255f); - - for (nuint i = 0; i < count; i++) - { - Vector4 s = Unsafe.Add(ref sBase, i); - s *= maxBytes; - s += half; - s = Numerics.Clamp(s, Vector4.Zero, maxBytes); - - ref ByteVector4 d = ref Unsafe.Add(ref dBase, i); - d.X = (byte)s.X; - d.Y = (byte)s.Y; - d.Z = (byte)s.Z; - d.W = (byte)s.W; - } - } - [StructLayout(LayoutKind.Sequential)] private struct ByteVector4 { diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index dff687fa1..53c26a57e 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -64,24 +64,6 @@ public abstract class FromVector4 public class FromVector4Rgba32 : FromVector4 { - [Benchmark] - public void FallbackIntrinsics128() - { - Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); - - SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats); - } - - [Benchmark] - public void ExtendedIntrinsic() - { - Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); - - SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); - } - [Benchmark] public void UseHwIntrinsics() { diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs index 1ceae8414..9c7ecbc49 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs @@ -30,15 +30,6 @@ public class ToVector4_Rgba32 : ToVector4 this.source.GetSpan(), this.destination.GetSpan()); - [Benchmark] - public void ExtendedIntrinsics() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - - SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); - } - [Benchmark] public void HwIntrinsics() { diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 200371679..e9e4550b0 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -122,12 +122,6 @@ public partial class SimdUtilsTests count, (s, d) => SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(s.Span, d.Span)); - [Theory] - [MemberData(nameof(ArraySizesDivisibleBy32))] - public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat(int count) => TestImpl_BulkConvertByteToNormalizedFloat( - count, - (s, d) => SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(s.Span, d.Span)); - [Theory] [MemberData(nameof(ArraySizesDivisibleBy32))] public void HwIntrinsics_BulkConvertByteToNormalizedFloat(int count) @@ -166,18 +160,6 @@ public partial class SimdUtilsTests Assert.Equal(expected, result, new ApproximateFloatComparer(1e-5f)); } - [Theory] - [MemberData(nameof(ArraySizesDivisibleBy4))] - public void FallbackIntrinsics128_BulkConvertNormalizedFloatToByteClampOverflows(int count) => TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( - count, - (s, d) => SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(s.Span, d.Span)); - - [Theory] - [MemberData(nameof(ArraySizesDivisibleBy32))] - public void ExtendedIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) => TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( - count, - (s, d) => SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(s.Span, d.Span)); - [Theory] [InlineData(1234)] public void ExtendedIntrinsics_ConvertToSingle(short scale) From 68b7dbcf58d7a944197a78bd40236c9d4057840b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Feb 2024 19:48:20 +1000 Subject: [PATCH 065/220] Update src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs Co-authored-by: Clinton Ingram --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index feb55ebe5..df844582a 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -49,10 +49,7 @@ internal static partial class SimdUtils 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 PermuteMaskShiftAlpha8x32() - => Vector256.Create( - 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, - 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); + public static Vector256 PermuteMaskShiftAlpha8x32() => Vector256.Create(0u, 1, 2, 4, 5, 6, 3, 7); #pragma warning restore SA1137 // Elements should have the same indentation #pragma warning restore SA1117 // Parameters should be on same line or separate lines From 9d737b71f758bd764e68c562db9e3a0d72221b21 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Feb 2024 19:51:01 +1000 Subject: [PATCH 066/220] Avoid downclocking --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index feb55ebe5..f5fa73cf0 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -896,7 +896,9 @@ internal static partial class SimdUtils { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - if (Avx512BW.IsSupported || Avx2.IsSupported || Sse2.IsSupported || AdvSimd.IsSupported) + if ((Vector512.IsHardwareAccelerated && Avx512BW.IsSupported) || + (Vector256.IsHardwareAccelerated && Avx2.IsSupported) || + (Vector128.IsHardwareAccelerated && (Sse2.IsSupported || AdvSimd.IsSupported))) { int remainder; From ecdd12e7b58ae6ef01947b849f844ca7e5b0b254 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 09:10:56 +0000 Subject: [PATCH 067/220] Bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/code-coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 62b6477ee..19a9689ce 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -81,7 +81,7 @@ jobs: path: tests/Images/ActualOutput/ - name: Codecov Update - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') with: flags: unittests From c6758df08b193cca5243ec030db5305c0846c3dd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2024 16:22:50 +1000 Subject: [PATCH 068/220] Optimize and cleanup ByteToNormalizedFloatReduce --- src/ImageSharp/Common/Helpers/Numerics.cs | 20 ++ .../Common/Helpers/SimdUtils.Convert.cs | 77 +++++++ .../Helpers/SimdUtils.ExtendedIntrinsics.cs | 38 ---- .../SimdUtils.FallbackIntrinsics128.cs | 83 -------- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 190 +++++++++++------- src/ImageSharp/Common/Helpers/SimdUtils.cs | 97 --------- .../Common/Helpers/Vector128Utilities.cs | 13 +- .../Formats/Jpeg/Components/Block8x8F.cs | 42 ++-- .../Utils/Vector4Converters.RgbaCompatible.cs | 75 ++++--- tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs | 25 +-- .../Bulk/ToVector4_Bgra32.cs | 4 +- .../Bulk/ToVector4_Rgb24.cs | 4 +- .../Bulk/ToVector4_Rgba32.cs | 63 +++--- .../LoadResizeSave/README.md | 2 +- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 36 +--- 15 files changed, 344 insertions(+), 425 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs delete mode 100644 src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs delete mode 100644 src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 5f85734e8..777de2dc9 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -1069,4 +1069,24 @@ internal static class Numerics public static nuint Vector256Count(int length) where TVector : struct => (uint)length / (uint)Vector256.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector512Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector512.Count; + + /// + /// Gets the count of vectors that safely fit into length. + /// + /// The type of the vector. + /// The given length. + /// Count of vectors that safely fit into the length. + public static nuint Vector512Count(int length) + where TVector : struct + => (uint)length / (uint)Vector512.Count; } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs new file mode 100644 index 000000000..1b5a418de --- /dev/null +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp; + +internal static partial class SimdUtils +{ + /// + /// Converts all input -s to -s normalized into [0..1]. + /// should be the of the same size as , + /// but there are no restrictions on the span's length. + /// + /// The source span of bytes + /// The destination span of floats + [MethodImpl(InliningOptions.ShortMethod)] + internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span destination) + { + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); + + HwIntrinsics.ByteToNormalizedFloatReduce(ref source, ref destination); + + if (source.Length > 0) + { + ConvertByteToNormalizedFloatRemainder(source, destination); + } + } + + /// + /// Convert all values normalized into [0..1] from 'source' into 'destination' buffer of . + /// The values are scaled up into [0-255] and rounded, overflows are clamped. + /// should be the of the same size as , + /// but there are no restrictions on the span's length. + /// + /// The source span of floats + /// The destination span of bytes + [MethodImpl(InliningOptions.ShortMethod)] + internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span destination) + { + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); + + HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination); + + if (source.Length > 0) + { + ConvertNormalizedFloatToByteRemainder(source, destination); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span destination) + { + ref byte sBase = ref MemoryMarshal.GetReference(source); + ref float dBase = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < source.Length; i++) + { + Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i) / 255f; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span destination) + { + ref float sBase = ref MemoryMarshal.GetReference(source); + ref byte dBase = ref MemoryMarshal.GetReference(destination); + for (int i = 0; i < source.Length; i++) + { + Unsafe.Add(ref dBase, i) = ConvertToByte(Unsafe.Add(ref sBase, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255F) + 0.5F, 0, 255F); +} diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs deleted file mode 100644 index 3c2f189cf..000000000 --- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -// ReSharper disable MemberHidesStaticFromOuterClass -namespace SixLabors.ImageSharp; - -internal static partial class SimdUtils -{ - /// - /// Implementation methods based on newer API-s (Vector.Widen, Vector.Narrow, Vector.ConvertTo*). - /// Only accelerated only on RyuJIT having dotnet/coreclr#10662 merged (.NET Core 2.1+ .NET 4.7.2+) - /// See: - /// https://github.com/dotnet/coreclr/pull/10662 - /// API Proposal: - /// https://github.com/dotnet/corefx/issues/15957 - /// - public static class ExtendedIntrinsics - { - public static bool IsAvailable { get; } = Vector.IsHardwareAccelerated; - - /// - /// Widen and convert a vector of values into 2 vectors of -s. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void ConvertToSingle( - Vector source, - out Vector dest1, - out Vector dest2) - { - Vector.Widen(source, out Vector i1, out Vector i2); - dest1 = Vector.ConvertToSingle(i1); - dest2 = Vector.ConvertToSingle(i2); - } - } -} diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs deleted file mode 100644 index fcf441c47..000000000 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// ReSharper disable MemberHidesStaticFromOuterClass -namespace SixLabors.ImageSharp; - -internal static partial class SimdUtils -{ - /// - /// Fallback implementation based on (128bit). - /// For , efficient software fallback implementations are present, - /// and we hope that even mono's JIT is able to emit SIMD instructions for that type :P - /// - public static class FallbackIntrinsics128 - { - /// - /// as many elements as possible, slicing them down (keeping the remainder). - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void ByteToNormalizedFloatReduce( - ref ReadOnlySpan source, - ref Span dest) - { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - int remainder = Numerics.Modulo4(source.Length); - int adjustedCount = source.Length - remainder; - - if (adjustedCount > 0) - { - ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]); - - source = source[adjustedCount..]; - dest = dest[adjustedCount..]; - } - } - - /// - /// Implementation of using . - /// - [MethodImpl(InliningOptions.ColdPath)] - internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) - { - DebugVerifySpanInput(source, dest, 4); - - uint count = (uint)dest.Length / 4; - if (count == 0) - { - return; - } - - ref ByteVector4 sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref Vector4 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - - const float scale = 1f / 255f; - Vector4 d = default; - - for (nuint i = 0; i < count; i++) - { - ref ByteVector4 s = ref Unsafe.Add(ref sBase, i); - d.X = s.X; - d.Y = s.Y; - d.Z = s.Z; - d.W = s.W; - d *= scale; - Unsafe.Add(ref dBase, i) = d; - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct ByteVector4 - { - public byte X; - public byte Y; - public byte Z; - public byte W; - } - } -} diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 487331360..6f0b4b4e3 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -752,17 +752,23 @@ internal static partial class SimdUtils /// /// as many elements as possible, slicing them down (keeping the remainder). /// + /// The source buffer. + /// The destination buffer. [MethodImpl(InliningOptions.ShortMethod)] internal static void ByteToNormalizedFloatReduce( ref ReadOnlySpan source, - ref Span dest) + ref Span destination) { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); if (Avx2.IsSupported || Sse2.IsSupported) { int remainder; - if (Avx2.IsSupported) + if (Vector512.IsHardwareAccelerated && Avx512F.IsSupported) + { + remainder = Numerics.ModuloP2(source.Length, Vector512.Count); + } + else if (Avx2.IsSupported) { remainder = Numerics.ModuloP2(source.Length, Vector256.Count); } @@ -775,10 +781,10 @@ internal static partial class SimdUtils if (adjustedCount > 0) { - ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]); + ByteToNormalizedFloat(source[..adjustedCount], destination[..adjustedCount]); source = source[adjustedCount..]; - dest = dest[adjustedCount..]; + destination = destination[adjustedCount..]; } } } @@ -786,97 +792,127 @@ internal static partial class SimdUtils /// /// Implementation , which is faster on new RyuJIT runtime. /// + /// The source buffer. + /// The destination buffer. /// /// Implementation is based on MagicScaler code: /// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L80-L182 /// internal static unsafe void ByteToNormalizedFloat( ReadOnlySpan source, - Span dest) + Span destination) { - fixed (byte* sourceBase = source) + if (Avx512F.IsSupported) { - if (Avx2.IsSupported) - { - DebugVerifySpanInput(source, dest, Vector256.Count); - - nuint n = dest.Vector256Count(); + DebugVerifySpanInput(source, destination, Vector512.Count); - ref Vector256 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + nuint n = destination.Vector512Count(); - Vector256 scale = Vector256.Create(1 / (float)byte.MaxValue); + ref byte sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - for (nuint i = 0; i < n; i++) - { - nuint si = (uint)Vector256.Count * i; - Vector256 i0 = Avx2.ConvertToVector256Int32(sourceBase + si); - Vector256 i1 = Avx2.ConvertToVector256Int32(sourceBase + si + Vector256.Count); - Vector256 i2 = Avx2.ConvertToVector256Int32(sourceBase + si + (Vector256.Count * 2)); - Vector256 i3 = Avx2.ConvertToVector256Int32(sourceBase + si + (Vector256.Count * 3)); - - Vector256 f0 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i0)); - Vector256 f1 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i1)); - Vector256 f2 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i2)); - Vector256 f3 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i3)); - - ref Vector256 d = ref Unsafe.Add(ref destBase, i * 4); - - d = f0; - Unsafe.Add(ref d, 1) = f1; - Unsafe.Add(ref d, 2) = f2; - Unsafe.Add(ref d, 3) = f3; - } + for (nuint i = 0; i < n; i++) + { + nuint si = (uint)Vector512.Count * i; + Vector512 i0 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si)); + Vector512 i1 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)Vector512.Count)); + Vector512 i2 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)(Vector512.Count * 2))); + Vector512 i3 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)(Vector512.Count * 3))); + + // Declare multiplier on each line. Codegen is better. + Vector512 f0 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i0); + Vector512 f1 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i1); + Vector512 f2 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i2); + Vector512 f3 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i3); + + ref Vector512 d = ref Unsafe.Add(ref destinationBase, i * 4); + + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; } - else + } + else if (Avx2.IsSupported) + { + DebugVerifySpanInput(source, destination, Vector256.Count); + + nuint n = destination.Vector256Count(); + + ref byte sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + + for (nuint i = 0; i < n; i++) { - // Sse - DebugVerifySpanInput(source, dest, Vector128.Count); + nuint si = (uint)Vector256.Count * i; + Vector256 i0 = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sourceBase, si)); + Vector256 i1 = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)Vector256.Count)); + Vector256 i2 = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)(Vector256.Count * 2))); + + // Ensure overreads past 16 byte boundary do not happen in debug due to lack of containment. + ref ulong refULong = ref Unsafe.As(ref Unsafe.Add(ref sourceBase, si)); + Vector256 i3 = Avx2.ConvertToVector256Int32(Vector128.CreateScalarUnsafe(Unsafe.Add(ref refULong, 3)).AsByte()); + + // Declare multiplier on each line. Codegen is better. + Vector256 f0 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i0); + Vector256 f1 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i1); + Vector256 f2 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i2); + Vector256 f3 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i3); + + ref Vector256 d = ref Unsafe.Add(ref destinationBase, i * 4); + + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; + } + } + else if (Sse2.IsSupported || AdvSimd.IsSupported) + { + DebugVerifySpanInput(source, destination, Vector128.Count); - nuint n = dest.Vector128Count(); + nuint n = destination.Vector128Count(); + + ref byte sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector128 destBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + Vector128 scale = Vector128.Create(1 / (float)byte.MaxValue); + Vector128 zero = Vector128.Zero; - Vector128 scale = Vector128.Create(1 / (float)byte.MaxValue); - Vector128 zero = Vector128.Zero; + for (nuint i = 0; i < n; i++) + { + nuint si = (uint)Vector128.Count * i; - for (nuint i = 0; i < n; i++) + Vector128 i0, i1, i2, i3; + if (Sse41.IsSupported) { - nuint si = (uint)Vector128.Count * i; - - Vector128 i0, i1, i2, i3; - if (Sse41.IsSupported) - { - i0 = Sse41.ConvertToVector128Int32(sourceBase + si); - i1 = Sse41.ConvertToVector128Int32(sourceBase + si + Vector128.Count); - i2 = Sse41.ConvertToVector128Int32(sourceBase + si + (Vector128.Count * 2)); - i3 = Sse41.ConvertToVector128Int32(sourceBase + si + (Vector128.Count * 3)); - } - else - { - Vector128 b = Sse2.LoadVector128(sourceBase + si); - Vector128 s0 = Sse2.UnpackLow(b, zero).AsInt16(); - Vector128 s1 = Sse2.UnpackHigh(b, zero).AsInt16(); - - i0 = Sse2.UnpackLow(s0, zero.AsInt16()).AsInt32(); - i1 = Sse2.UnpackHigh(s0, zero.AsInt16()).AsInt32(); - i2 = Sse2.UnpackLow(s1, zero.AsInt16()).AsInt32(); - i3 = Sse2.UnpackHigh(s1, zero.AsInt16()).AsInt32(); - } - - Vector128 f0 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i0)); - Vector128 f1 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i1)); - Vector128 f2 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i2)); - Vector128 f3 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i3)); - - ref Vector128 d = ref Unsafe.Add(ref destBase, i * 4); - - d = f0; - Unsafe.Add(ref d, 1) = f1; - Unsafe.Add(ref d, 2) = f2; - Unsafe.Add(ref d, 3) = f3; + ref int refInt = ref Unsafe.As(ref Unsafe.Add(ref sourceBase, si)); + + i0 = Sse41.ConvertToVector128Int32(Vector128.CreateScalarUnsafe(refInt).AsByte()); + i1 = Sse41.ConvertToVector128Int32(Vector128.CreateScalarUnsafe(Unsafe.Add(ref refInt, 1)).AsByte()); + i2 = Sse41.ConvertToVector128Int32(Vector128.CreateScalarUnsafe(Unsafe.Add(ref refInt, 2)).AsByte()); + i3 = Sse41.ConvertToVector128Int32(Vector128.CreateScalarUnsafe(Unsafe.Add(ref refInt, 3)).AsByte()); } + else + { + // Sse2, AdvSimd + Vector128 b = Vector128.LoadUnsafe(ref sourceBase, si); + (Vector128 s0, Vector128 s1) = Vector128.Widen(b); + (i0, i1) = Vector128.Widen(s0.AsInt16()); + (i2, i3) = Vector128.Widen(s1.AsInt16()); + } + + Vector128 f0 = scale * Vector128.ConvertToSingle(i0); + Vector128 f1 = scale * Vector128.ConvertToSingle(i1); + Vector128 f2 = scale * Vector128.ConvertToSingle(i2); + Vector128 f3 = scale * Vector128.ConvertToSingle(i3); + + ref Vector128 d = ref Unsafe.Add(ref destinationBase, i * 4); + + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; } } } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 002c1f8da..0279e57cc 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -22,13 +22,6 @@ internal static partial class SimdUtils public static bool HasVector8 { get; } = Vector.IsHardwareAccelerated && Vector.Count == 8 && Vector.Count == 8; - /// - /// Gets a value indicating whether code is being JIT-ed to SSE instructions - /// where float and integer registers are of size 128 byte. - /// - public static bool HasVector4 { get; } = - Vector.IsHardwareAccelerated && Vector.Count == 4; - /// /// Transform all scalars in 'v' in a way that converting them to would have rounding semantics. /// @@ -69,96 +62,6 @@ internal static partial class SimdUtils } } - /// - /// Converts all input -s to -s normalized into [0..1]. - /// should be the of the same size as , - /// but there are no restrictions on the span's length. - /// - /// The source span of bytes - /// The destination span of floats - [MethodImpl(InliningOptions.ShortMethod)] - internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) - { - DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); - - HwIntrinsics.ByteToNormalizedFloatReduce(ref source, ref dest); - - // Also deals with the remainder from previous conversions: - FallbackIntrinsics128.ByteToNormalizedFloatReduce(ref source, ref dest); - - // Deal with the remainder: - if (source.Length > 0) - { - ConvertByteToNormalizedFloatRemainder(source, dest); - } - } - - /// - /// Convert all values normalized into [0..1] from 'source' into 'destination' buffer of . - /// The values are scaled up into [0-255] and rounded, overflows are clamped. - /// should be the of the same size as , - /// but there are no restrictions on the span's length. - /// - /// The source span of floats - /// The destination span of bytes - [MethodImpl(InliningOptions.ShortMethod)] - internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span destination) - { - DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination); - - // Deal with the remainder: - if (source.Length > 0) - { - ConvertNormalizedFloatToByteRemainder(source, destination); - } - } - - [MethodImpl(InliningOptions.ColdPath)] - private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span destination) - { - ref byte sBase = ref MemoryMarshal.GetReference(source); - ref float dBase = ref MemoryMarshal.GetReference(destination); - - // There are at most 3 elements at this point, having a for loop is overkill. - // Let's minimize the no. of instructions! - switch (source.Length) - { - case 3: - Unsafe.Add(ref dBase, 2) = Unsafe.Add(ref sBase, 2) / 255f; - goto case 2; - case 2: - Unsafe.Add(ref dBase, 1) = Unsafe.Add(ref sBase, 1) / 255f; - goto case 1; - case 1: - dBase = sBase / 255f; - break; - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span destination) - { - ref float sBase = ref MemoryMarshal.GetReference(source); - ref byte dBase = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < source.Length; i++) - { - Unsafe.Add(ref dBase, i) = ConvertToByte(Unsafe.Add(ref sBase, i)); - } - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255F) + 0.5F, 0, 255F); - - [Conditional("DEBUG")] - private static void VerifyHasVector8(string operation) - { - if (!HasVector8) - { - throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!"); - } - } - [Conditional("DEBUG")] private static void DebugVerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) { diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index a07fa8ca6..b6dd319f0 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -26,7 +26,7 @@ internal static class Vector128Utilities public static bool SupportsShuffleFloat { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Sse.IsSupported || AdvSimd.IsSupported; + get => Sse.IsSupported; } /// @@ -70,17 +70,6 @@ internal static class Vector128Utilities return Sse.Shuffle(vector, vector, control); } - if (AdvSimd.IsSupported) - { -#pragma warning disable CA1857 // A constant is expected for the parameter - Vector128 result = Vector128.Create(AdvSimd.Extract(vector, (byte)(control & 0x3))); - result = AdvSimd.Insert(result, 1, AdvSimd.Extract(vector, (byte)((control >> 2) & 0x3))); - result = AdvSimd.Insert(result, 2, AdvSimd.Extract(vector, (byte)((control >> 4) & 0x3))); - result = AdvSimd.Insert(result, 3, AdvSimd.Extract(vector, (byte)((control >> 6) & 0x3))); -#pragma warning restore CA1857 // A constant is expected for the parameter - return result; - } - ThrowUnreachableException(); return default; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index d432e82d2..018df5f9f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -386,29 +386,33 @@ internal partial struct Block8x8F : IEquatable public void LoadFromInt16ExtendedAvx2(ref Block8x8 source) { DebugGuard.IsTrue( - SimdUtils.HasVector8, + Avx2.IsSupported, "LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!"); - ref Vector sRef = ref Unsafe.As>(ref source); - ref Vector dRef = ref Unsafe.As>(ref this); + ref short sRef = ref Unsafe.As(ref source); + ref Vector256 dRef = ref Unsafe.As>(ref this); - // Vector.Count == 16 on AVX2 + // Vector256.Count == 16 on AVX2 // We can process 2 block rows in a single step - SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector top, out Vector bottom); - dRef = top; - Unsafe.Add(ref dRef, 1) = bottom; - - SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom); - Unsafe.Add(ref dRef, 2) = top; - Unsafe.Add(ref dRef, 3) = bottom; - - SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom); - Unsafe.Add(ref dRef, 4) = top; - Unsafe.Add(ref dRef, 5) = bottom; - - SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom); - Unsafe.Add(ref dRef, 6) = top; - Unsafe.Add(ref dRef, 7) = bottom; + Vector256 top = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef)); + Vector256 bottom = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)Vector256.Count)); + dRef = Avx.ConvertToVector256Single(top); + Unsafe.Add(ref dRef, 1) = Avx.ConvertToVector256Single(bottom); + + top = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 2))); + bottom = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 3))); + Unsafe.Add(ref dRef, 2) = Avx.ConvertToVector256Single(top); + Unsafe.Add(ref dRef, 3) = Avx.ConvertToVector256Single(bottom); + + top = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 4))); + bottom = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 5))); + Unsafe.Add(ref dRef, 4) = Avx.ConvertToVector256Single(top); + Unsafe.Add(ref dRef, 5) = Avx.ConvertToVector256Single(bottom); + + top = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 6))); + bottom = Avx2.ConvertToVector256Int32(Vector128.LoadUnsafe(ref sRef, (nuint)(Vector256.Count * 7))); + Unsafe.Add(ref dRef, 6) = Avx.ConvertToVector256Single(top); + Unsafe.Add(ref dRef, 7) = Avx.ConvertToVector256Single(bottom); } /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 8616ecb3b..9e649f3c0 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats.Utils; @@ -31,74 +32,86 @@ internal static partial class Vector4Converters /// Provides an efficient default implementation for /// The method works by internally converting to a therefore it's not applicable for that type! /// - [MethodImpl(InliningOptions.ShortMethod)] + /// The type of pixel format. + /// The configuration. + /// The pixel operations instance. + /// The source buffer. + /// The destination buffer. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ToVector4( Configuration configuration, PixelOperations pixelOperations, - ReadOnlySpan sourcePixels, - Span destVectors, + ReadOnlySpan source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = sourcePixels.Length; + int count = source.Length; // Not worth for small buffers: if (count < Vector4ConversionThreshold) { - Default.UnsafeToVector4(sourcePixels, destVectors, modifiers); + Default.UnsafeToVector4(source, destination, modifiers); return; } - // Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation: + // Using the last quarter of 'destination' as a temporary buffer to avoid allocation: int countWithoutLastItem = count - 1; - ReadOnlySpan reducedSource = sourcePixels[..countWithoutLastItem]; - Span lastQuarterOfDestBuffer = MemoryMarshal.Cast(destVectors).Slice((3 * count) + 1, countWithoutLastItem); - pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer); + ReadOnlySpan reducedSource = source[..countWithoutLastItem]; + Span lastQuarterOfDestination = MemoryMarshal.Cast(destination).Slice((3 * count) + 1, countWithoutLastItem); + pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestination); - // 'destVectors' and 'lastQuarterOfDestBuffer' are overlapping buffers, + // 'destination' and 'lastQuarterOfDestination' are overlapping buffers, // but we are always reading/writing at different positions: SimdUtils.ByteToNormalizedFloat( - MemoryMarshal.Cast(lastQuarterOfDestBuffer), - MemoryMarshal.Cast(destVectors[..countWithoutLastItem])); + MemoryMarshal.Cast(lastQuarterOfDestination), + MemoryMarshal.Cast(destination[..countWithoutLastItem])); - destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); + destination[countWithoutLastItem] = source[countWithoutLastItem].ToVector4(); // TODO: Investigate optimized 1-pass approach! - ApplyForwardConversionModifiers(destVectors, modifiers); + ApplyForwardConversionModifiers(destination, modifiers); } /// /// Provides an efficient default implementation for /// The method is works by internally converting to a therefore it's not applicable for that type! /// - [MethodImpl(InliningOptions.ShortMethod)] + /// The type of pixel format. + /// The configuration. + /// The pixel operations instance. + /// The source buffer. + /// The destination buffer. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void FromVector4( Configuration configuration, PixelOperations pixelOperations, - Span sourceVectors, - Span destPixels, + Span source, + Span destination, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, nameof(configuration)); - Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = sourceVectors.Length; + int count = source.Length; // Not worth for small buffers: if (count < Vector4ConversionThreshold) { - Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers); + Default.UnsafeFromVector4(source, destination, modifiers); return; } // TODO: Investigate optimized 1-pass approach! - ApplyBackwardConversionModifiers(sourceVectors, modifiers); + ApplyBackwardConversionModifiers(source, modifiers); // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion, // so let's allocate a temporary buffer as usually: @@ -106,20 +119,30 @@ internal static partial class Vector4Converters Span tempSpan = tempBuffer.Memory.Span; SimdUtils.NormalizedFloatToByteSaturate( - MemoryMarshal.Cast(sourceVectors), + MemoryMarshal.Cast(source), MemoryMarshal.Cast(tempSpan)); - pixelOperations.FromRgba32(configuration, tempSpan, destPixels); + pixelOperations.FromRgba32(configuration, tempSpan, destination); } private static int CalculateVector4ConversionThreshold() { - if (!Vector.IsHardwareAccelerated) + if (!Vector128.IsHardwareAccelerated) { return int.MaxValue; } - return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.HasVector8 ? 256 : 128; + if (Vector512.IsHardwareAccelerated) + { + return 512; + } + + if (Vector256.IsHardwareAccelerated) + { + return 256; + } + + return 128; } } } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs index 3b4360b16..0df8d9818 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class ToVector4 where TPixel : unmanaged, IPixel { - protected IMemoryOwner source; + protected IMemoryOwner Source { get; set; } - protected IMemoryOwner destination; + protected IMemoryOwner Destination { get; set; } protected Configuration Configuration => Configuration.Default; @@ -26,22 +26,22 @@ public abstract class ToVector4 [GlobalSetup] public void Setup() { - this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); - this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.Source = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.Destination = this.Configuration.MemoryAllocator.Allocate(this.Count); } [GlobalCleanup] public void Cleanup() { - this.source.Dispose(); - this.destination.Dispose(); + this.Source.Dispose(); + this.Destination.Dispose(); } // [Benchmark] public void Naive() { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); + Span s = this.Source.GetSpan(); + Span d = this.Destination.GetSpan(); for (int i = 0; i < this.Count; i++) { @@ -50,11 +50,8 @@ public abstract class ToVector4 } [Benchmark] - public void PixelOperations_Specialized() - { - PixelOperations.Instance.ToVector4( + public void PixelOperations_Specialized() => PixelOperations.Instance.ToVector4( this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); - } + this.Source.GetSpan(), + this.Destination.GetSpan()); } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs index 934a17dc9..6499632b6 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs @@ -16,8 +16,8 @@ public class ToVector4_Bgra32 : ToVector4 { new PixelOperations().ToVector4( this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + this.Source.GetSpan(), + this.Destination.GetSpan()); } // RESULTS: diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs index d5d6e31b5..adedabf8f 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs @@ -16,8 +16,8 @@ public class ToVector4_Rgb24 : ToVector4 { new PixelOperations().ToVector4( this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + this.Source.GetSpan(), + this.Destination.GetSpan()); } } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs index 9c7ecbc49..113793a03 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs @@ -14,27 +14,18 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.Short))] public class ToVector4_Rgba32 : ToVector4 { - [Benchmark] - public void FallbackIntrinsics128() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - - SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(sBytes, dFloats); - } - [Benchmark] public void PixelOperations_Base() => new PixelOperations().ToVector4( this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + this.Source.GetSpan(), + this.Destination.GetSpan()); [Benchmark] public void HwIntrinsics() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); } @@ -42,8 +33,8 @@ public class ToVector4_Rgba32 : ToVector4 // [Benchmark] public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); nuint n = (uint)dFloats.Length / (uint)Vector.Count; @@ -67,14 +58,14 @@ public class ToVector4_Rgba32 : ToVector4 } n = (uint)(dFloats.Length / Vector.Count); - var scale = new Vector(1f / 255f); + Vector scale = new(1f / 255f); for (nuint i = 0; i < n; i++) { ref Vector dRef = ref Unsafe.Add(ref destBase, i); - var du = Vector.AsVectorInt32(dRef); - var v = Vector.ConvertToSingle(du); + Vector du = Vector.AsVectorInt32(dRef); + Vector v = Vector.ConvertToSingle(du); v *= scale; dRef = v; @@ -84,14 +75,14 @@ public class ToVector4_Rgba32 : ToVector4 // [Benchmark] public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_ConvertInSameLoop() { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); nuint n = (uint)dFloats.Length / (uint)Vector.Count; ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); - var scale = new Vector(1f / 255f); + Vector scale = new(1f / 255f); for (nuint i = 0; i < n; i++) { @@ -117,8 +108,8 @@ public class ToVector4_Rgba32 : ToVector4 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector ConvertToNormalizedSingle(Vector u, Vector scale) { - var vi = Vector.AsVectorInt32(u); - var v = Vector.ConvertToSingle(vi); + Vector vi = Vector.AsVectorInt32(u); + Vector v = Vector.ConvertToSingle(vi); v *= scale; return v; } @@ -151,4 +142,30 @@ public class ToVector4_Rgba32 : ToVector4 PixelOperations_Base | Core | 2048 | 6,752.68 ns | 272.820 ns | 15.4148 ns | 1.67 | 0.02 | - | 24 B | PixelOperations_Specialized | Core | 2048 | 1,126.13 ns | 79.192 ns | 4.4745 ns |!! 0.28 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! */ + + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3085/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + Job-DFEQJT : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + Runtime=.NET 8.0 Arguments=/p:DebugType=portable IterationCount=3 + LaunchCount=1 WarmupCount=3 + + | Method | Count | Mean | Error | StdDev | Allocated | + |---------------------------- |------ |------------:|-----------:|----------:|----------:| + | FallbackIntrinsics128 | 64 | 139.66 ns | 27.429 ns | 1.503 ns | - | + | PixelOperations_Base | 64 | 124.65 ns | 29.653 ns | 1.625 ns | - | + | HwIntrinsics | 64 | 18.16 ns | 4.731 ns | 0.259 ns | - | + | PixelOperations_Specialized | 64 | 27.94 ns | 15.220 ns | 0.834 ns | - | + | FallbackIntrinsics128 | 256 | 525.07 ns | 34.397 ns | 1.885 ns | - | + | PixelOperations_Base | 256 | 464.17 ns | 46.897 ns | 2.571 ns | - | + | HwIntrinsics | 256 | 43.88 ns | 4.525 ns | 0.248 ns | - | + | PixelOperations_Specialized | 256 | 55.57 ns | 14.587 ns | 0.800 ns | - | + | FallbackIntrinsics128 | 2048 | 4,148.44 ns | 476.583 ns | 26.123 ns | - | + | PixelOperations_Base | 2048 | 3,608.42 ns | 66.293 ns | 3.634 ns | - | + | HwIntrinsics | 2048 | 361.42 ns | 35.576 ns | 1.950 ns | - | + | PixelOperations_Specialized | 2048 | 374.82 ns | 33.371 ns | 1.829 ns | - | + */ } diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/README.md b/tests/ImageSharp.Benchmarks/LoadResizeSave/README.md index 6cb48eb48..98f472241 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/README.md +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/README.md @@ -1,4 +1,4 @@ -The benchmarks have been adapted from the +The benchmarks have been adapted from the [PhotoSauce's MemoryStress project](https://github.com/saucecontrol/core-imaging-playground/tree/beeees/MemoryStress). ### Setup diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index e9e4550b0..36b301264 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -117,16 +118,10 @@ public partial class SimdUtilsTests public static readonly TheoryData ArbitraryArraySizes = new() { 0, 1, 2, 3, 4, 7, 8, 9, 15, 16, 17, 63, 64, 255, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520 }; [Theory] - [MemberData(nameof(ArraySizesDivisibleBy4))] - public void FallbackIntrinsics128_BulkConvertByteToNormalizedFloat(int count) => TestImpl_BulkConvertByteToNormalizedFloat( - count, - (s, d) => SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(s.Span, d.Span)); - - [Theory] - [MemberData(nameof(ArraySizesDivisibleBy32))] + [MemberData(nameof(ArraySizesDivisibleBy64))] public void HwIntrinsics_BulkConvertByteToNormalizedFloat(int count) { - if (!Sse2.IsSupported) + if (!Sse2.IsSupported && !AdvSimd.IsSupported) { return; } @@ -138,7 +133,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE41); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE41); } [Theory] @@ -160,32 +155,11 @@ public partial class SimdUtilsTests Assert.Equal(expected, result, new ApproximateFloatComparer(1e-5f)); } - [Theory] - [InlineData(1234)] - public void ExtendedIntrinsics_ConvertToSingle(short scale) - { - int n = Vector.Count; - short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale); - float[] fData = sData.Select(u => (float)u).ToArray(); - - Vector source = new(sData); - - Vector expected1 = new(fData, 0); - Vector expected2 = new(fData, n); - - // Act: - SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector actual1, out Vector actual2); - - // Assert: - Assert.Equal(expected1, actual1); - Assert.Equal(expected2, actual2); - } - [Theory] [MemberData(nameof(ArraySizesDivisibleBy64))] public void HwIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) { - if (!Sse2.IsSupported) + if (!Sse2.IsSupported && !AdvSimd.IsSupported) { return; } From 0ec839fb931f3d1096c91c397857c31bf888b354 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2024 17:34:13 +1000 Subject: [PATCH 069/220] Update SimdUtils.HwIntrinsics.cs --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 6f0b4b4e3..6e45f1619 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -761,10 +761,13 @@ internal static partial class SimdUtils { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - if (Avx2.IsSupported || Sse2.IsSupported) + if ((Vector512.IsHardwareAccelerated && Avx512F.IsSupported) || + Avx2.IsSupported || + Sse2.IsSupported || + AdvSimd.IsSupported) { int remainder; - if (Vector512.IsHardwareAccelerated && Avx512F.IsSupported) + if (Avx512F.IsSupported) { remainder = Numerics.ModuloP2(source.Length, Vector512.Count); } @@ -877,7 +880,6 @@ internal static partial class SimdUtils ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); Vector128 scale = Vector128.Create(1 / (float)byte.MaxValue); - Vector128 zero = Vector128.Zero; for (nuint i = 0; i < n; i++) { From c10863fee66d11cd4d9d0b030c4f7235dc3c1b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Tue, 6 Feb 2024 15:33:11 +0100 Subject: [PATCH 070/220] Replace Memory with string --- .../Formats/Jpeg/JpegDecoderCore.cs | 5 ++--- .../Formats/Jpeg/JpegEncoderCore.cs | 22 +++++++++---------- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 4 ++-- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 2 +- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 10 ++++----- .../Formats/Jpg/JpegMetadataTests.cs | 10 ++++----- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index d6b40fa7f..5f3fa33fa 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -524,13 +524,12 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize) { Span temp = new byte[markerContentByteSize]; - char[] chars = new char[markerContentByteSize]; JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); stream.Read(temp); - Encoding.ASCII.GetChars(temp, chars); + string comment = Encoding.ASCII.GetString(temp); - metadata.Comments.Add(chars); + metadata.Comments.Add(comment); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 4dc920207..8658f6b25 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -186,23 +186,23 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals for (int i = 0; i < metadata.Comments.Count; i++) { - Memory chars = metadata.Comments[i]; + string comment = metadata.Comments[i]; - if (chars.Length > maxCommentLength) + if (comment.Length > maxCommentLength) { - Memory splitComment = chars.Slice(maxCommentLength, chars.Length - maxCommentLength); + string splitComment = comment.Substring(maxCommentLength, comment.Length - maxCommentLength); metadata.Comments.Insert(i + 1, splitComment); // We don't want to keep the extra bytes - chars = chars.Slice(0, maxCommentLength); + comment = comment.Substring(0, maxCommentLength); } - int commentLength = chars.Length + 4; + int commentLength = comment.Length + 4; - Span comment = new byte[commentLength]; - Span markers = comment.Slice(0, 2); - Span payloadSize = comment.Slice(2, 2); - Span payload = comment.Slice(4, chars.Length); + Span commentSpan = new byte[commentLength]; + Span markers = commentSpan.Slice(0, 2); + Span payloadSize = commentSpan.Slice(2, 2); + Span payload = commentSpan.Slice(4, comment.Length); // Beginning of comment ff fe markers[0] = JpegConstants.Markers.XFF; @@ -213,9 +213,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals payloadSize[0] = (byte)((comWithoutMarker >> 8) & 0xFF); payloadSize[1] = (byte)(comWithoutMarker & 0xFF); - Encoding.ASCII.GetBytes(chars.Span, payload); + Encoding.ASCII.GetBytes(comment, payload); - this.outputStream.Write(comment, 0, comment.Length); + this.outputStream.Write(commentSpan, 0, commentSpan.Length); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index bf758dfd0..5b96fdf96 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -15,7 +15,7 @@ public class JpegMetadata : IDeepCloneable /// public JpegMetadata() { - this.Comments = new List>(); + this.Comments = new List(); } /// @@ -106,7 +106,7 @@ public class JpegMetadata : IDeepCloneable /// /// Gets the comments. /// - public IList> Comments { get; } + public IList Comments { get; } /// public IDeepCloneable DeepClone() => new JpegMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 369e71abf..222d1fb8c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -435,7 +435,7 @@ public partial class JpegDecoderTests JpegMetadata metadata = image.Metadata.GetJpegMetadata(); Assert.Equal(1, metadata.Comments.Count); - Assert.Equal(expectedComment.ToCharArray(), metadata.Comments.ElementAtOrDefault(0)); + Assert.Equal(expectedComment, metadata.Comments.ElementAtOrDefault(0)); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 8cc64acea..f33234e32 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -172,7 +172,7 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(1, actual.Comments.Count); - Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0)); } [Fact] @@ -184,8 +184,8 @@ public partial class JpegEncoderTests using var memStream = new MemoryStream(); // act - meta.Comments.Add("First comment".ToCharArray()); - meta.Comments.Add("Second Comment".ToCharArray()); + meta.Comments.Add("First comment"); + meta.Comments.Add("Second Comment"); input.Save(memStream, JpegEncoder); // assert @@ -194,8 +194,8 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(2, actual.Comments.Count); - Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString()); - Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); + Assert.Equal(meta.Comments.ElementAtOrDefault(0), actual.Comments.ElementAtOrDefault(0)); + Assert.Equal(meta.Comments.ElementAtOrDefault(1), actual.Comments.ElementAtOrDefault(1)); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 8b991228b..64d7edd75 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -64,19 +64,19 @@ public class JpegMetadataTests { var meta = new JpegMetadata(); - Assert.True(Array.Empty>().SequenceEqual(meta.Comments)); + Assert.True(Array.Empty().SequenceEqual(meta.Comments)); } [Fact] public void Comment_OnlyComment() { string comment = "test comment"; - var expectedCollection = new Collection> { new(comment.ToCharArray()) }; + var expectedCollection = new Collection { comment }; var meta = new JpegMetadata(); - meta.Comments?.Add(comment.ToCharArray()); + meta.Comments.Add(comment); - Assert.Equal(1, meta.Comments?.Count); - Assert.True(expectedCollection.FirstOrDefault().ToString() == meta.Comments?.FirstOrDefault().ToString()); + Assert.Equal(1, meta.Comments.Count); + Assert.True(expectedCollection.FirstOrDefault() == meta.Comments.FirstOrDefault()); } } From d2251287ceaeb0c827d62b29a651775fd2daaa83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Tue, 6 Feb 2024 16:16:35 +0100 Subject: [PATCH 071/220] Introduce JpegComData.cs --- src/ImageSharp/Formats/Jpeg/JpegComData.cs | 32 +++++++++++++++++++ .../Formats/Jpeg/JpegDecoderCore.cs | 2 +- .../Formats/Jpeg/JpegEncoderCore.cs | 4 +-- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 4 +-- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 2 +- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 10 +++--- .../Formats/Jpg/JpegMetadataTests.cs | 6 ++-- 7 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/JpegComData.cs diff --git a/src/ImageSharp/Formats/Jpeg/JpegComData.cs b/src/ImageSharp/Formats/Jpeg/JpegComData.cs new file mode 100644 index 000000000..26ade0217 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegComData.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Jpeg; + +/// +/// Contains JPEG comment +/// +public readonly struct JpegComData +{ + /// + /// Converts string to + /// + /// The comment string. + /// The + public static JpegComData FromString(string value) => new(value.AsMemory()); + + /// + /// Initializes a new instance of the struct. + /// + /// The comment ReadOnlyMemory of chars. + public JpegComData(ReadOnlyMemory value) + => this.Value = value; + + public ReadOnlyMemory Value { get; } + + /// + /// Converts Value to string + /// + /// The comment string. + public override string ToString() => this.Value.ToString(); +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 5f3fa33fa..e43c20a7d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -529,7 +529,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals stream.Read(temp); string comment = Encoding.ASCII.GetString(temp); - metadata.Comments.Add(comment); + metadata.Comments.Add(JpegComData.FromString(comment)); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 8658f6b25..a55435c53 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -186,12 +186,12 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals for (int i = 0; i < metadata.Comments.Count; i++) { - string comment = metadata.Comments[i]; + string comment = metadata.Comments[i].ToString(); if (comment.Length > maxCommentLength) { string splitComment = comment.Substring(maxCommentLength, comment.Length - maxCommentLength); - metadata.Comments.Insert(i + 1, splitComment); + metadata.Comments.Insert(i + 1, JpegComData.FromString(splitComment)); // We don't want to keep the extra bytes comment = comment.Substring(0, maxCommentLength); diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 5b96fdf96..fe1324a86 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -15,7 +15,7 @@ public class JpegMetadata : IDeepCloneable /// public JpegMetadata() { - this.Comments = new List(); + this.Comments = new List(); } /// @@ -106,7 +106,7 @@ public class JpegMetadata : IDeepCloneable /// /// Gets the comments. /// - public IList Comments { get; } + public IList Comments { get; } /// public IDeepCloneable DeepClone() => new JpegMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 222d1fb8c..cbb2befcd 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -435,7 +435,7 @@ public partial class JpegDecoderTests JpegMetadata metadata = image.Metadata.GetJpegMetadata(); Assert.Equal(1, metadata.Comments.Count); - Assert.Equal(expectedComment, metadata.Comments.ElementAtOrDefault(0)); + Assert.Equal(expectedComment, metadata.Comments.ElementAtOrDefault(0).ToString()); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index f33234e32..e49afedbb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -172,7 +172,7 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(1, actual.Comments.Count); - Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0)); + Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0).ToString()); } [Fact] @@ -184,8 +184,8 @@ public partial class JpegEncoderTests using var memStream = new MemoryStream(); // act - meta.Comments.Add("First comment"); - meta.Comments.Add("Second Comment"); + meta.Comments.Add(JpegComData.FromString("First comment")); + meta.Comments.Add(JpegComData.FromString("Second Comment")); input.Save(memStream, JpegEncoder); // assert @@ -194,8 +194,8 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(2, actual.Comments.Count); - Assert.Equal(meta.Comments.ElementAtOrDefault(0), actual.Comments.ElementAtOrDefault(0)); - Assert.Equal(meta.Comments.ElementAtOrDefault(1), actual.Comments.ElementAtOrDefault(1)); + Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 64d7edd75..e07c42f89 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -64,7 +64,7 @@ public class JpegMetadataTests { var meta = new JpegMetadata(); - Assert.True(Array.Empty().SequenceEqual(meta.Comments)); + Assert.True(Array.Empty().SequenceEqual(meta.Comments)); } [Fact] @@ -74,9 +74,9 @@ public class JpegMetadataTests var expectedCollection = new Collection { comment }; var meta = new JpegMetadata(); - meta.Comments.Add(comment); + meta.Comments.Add(JpegComData.FromString(comment)); Assert.Equal(1, meta.Comments.Count); - Assert.True(expectedCollection.FirstOrDefault() == meta.Comments.FirstOrDefault()); + Assert.True(expectedCollection.FirstOrDefault() == meta.Comments.FirstOrDefault().ToString()); } } From 4e494d427f667c94fb59bcad972ac2f45410e826 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 14 Feb 2024 11:55:07 +1000 Subject: [PATCH 072/220] Simplify checks --- .../Common/Helpers/SimdUtils.Convert.cs | 7 ++--- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 26 +++++++------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs index 1b5a418de..5318ad049 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs @@ -57,7 +57,7 @@ internal static partial class SimdUtils for (int i = 0; i < source.Length; i++) { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i) / 255f; + Unsafe.Add(ref dBase, (uint)i) = Unsafe.Add(ref sBase, (uint)i) / 255f; } } @@ -66,12 +66,13 @@ internal static partial class SimdUtils { ref float sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(destination); + for (int i = 0; i < source.Length; i++) { - Unsafe.Add(ref dBase, i) = ConvertToByte(Unsafe.Add(ref sBase, i)); + Unsafe.Add(ref dBase, (uint)i) = ConvertToByte(Unsafe.Add(ref sBase, (uint)i)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255F) + 0.5F, 0, 255F); + private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255f) + 0.5f, 0, 255f); } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 6e45f1619..17ccb396d 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -761,13 +761,10 @@ internal static partial class SimdUtils { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - if ((Vector512.IsHardwareAccelerated && Avx512F.IsSupported) || - Avx2.IsSupported || - Sse2.IsSupported || - AdvSimd.IsSupported) + if (Vector128.IsHardwareAccelerated) { int remainder; - if (Avx512F.IsSupported) + if (Vector512.IsHardwareAccelerated && Avx512F.IsSupported) { remainder = Numerics.ModuloP2(source.Length, Vector512.Count); } @@ -805,7 +802,7 @@ internal static partial class SimdUtils ReadOnlySpan source, Span destination) { - if (Avx512F.IsSupported) + if (Vector512.IsHardwareAccelerated && Avx512F.IsSupported) { DebugVerifySpanInput(source, destination, Vector512.Count); @@ -870,7 +867,7 @@ internal static partial class SimdUtils Unsafe.Add(ref d, 3) = f3; } } - else if (Sse2.IsSupported || AdvSimd.IsSupported) + else if (Vector128.IsHardwareAccelerated) { DebugVerifySpanInput(source, destination, Vector128.Count); @@ -897,7 +894,7 @@ internal static partial class SimdUtils } else { - // Sse2, AdvSimd + // Sse2, AdvSimd, etc Vector128 b = Vector128.LoadUnsafe(ref sourceBase, si); (Vector128 s0, Vector128 s1) = Vector128.Widen(b); (i0, i1) = Vector128.Widen(s0.AsInt16()); @@ -931,13 +928,11 @@ internal static partial class SimdUtils { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - if ((Vector512.IsHardwareAccelerated && Avx512BW.IsSupported) || - (Vector256.IsHardwareAccelerated && Avx2.IsSupported) || - (Vector128.IsHardwareAccelerated && (Sse2.IsSupported || AdvSimd.IsSupported))) + if (Sse2.IsSupported || AdvSimd.IsSupported) { int remainder; - if (Avx512BW.IsSupported) + if (Vector512.IsHardwareAccelerated && Avx512BW.IsSupported) { remainder = Numerics.ModuloP2(source.Length, Vector512.Count); } @@ -977,7 +972,7 @@ internal static partial class SimdUtils ReadOnlySpan source, Span destination) { - if (Avx512BW.IsSupported) + if (Vector512.IsHardwareAccelerated && Avx512BW.IsSupported) { DebugVerifySpanInput(source, destination, Vector512.Count); @@ -1011,8 +1006,7 @@ internal static partial class SimdUtils Unsafe.Add(ref destinationBase, i) = b; } } - else - if (Avx2.IsSupported) + else if (Avx2.IsSupported) { DebugVerifySpanInput(source, destination, Vector256.Count); @@ -1046,7 +1040,7 @@ internal static partial class SimdUtils Unsafe.Add(ref destinationBase, i) = b; } } - else + else if (Sse2.IsSupported || AdvSimd.IsSupported) { // Sse, AdvSimd DebugVerifySpanInput(source, destination, Vector128.Count); From d865ab96f27d07435355d858fa67fb6fbad028ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:00:47 +0000 Subject: [PATCH 073/220] Bump NuGet/setup-nuget from 1 to 2 Bumps [NuGet/setup-nuget](https://github.com/nuget/setup-nuget) from 1 to 2. - [Release notes](https://github.com/nuget/setup-nuget/releases) - [Commits](https://github.com/nuget/setup-nuget/compare/v1...v2) --- updated-dependencies: - dependency-name: NuGet/setup-nuget dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 4 ++-- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 57488a1d0..52a52c472 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -77,7 +77,7 @@ jobs: run: git lfs pull - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v2 - name: NuGet Setup Cache uses: actions/cache@v4 @@ -159,7 +159,7 @@ jobs: submodules: recursive - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v2 - name: NuGet Setup Cache uses: actions/cache@v4 diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 58e19631f..d531e6860 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -44,7 +44,7 @@ jobs: run: git lfs pull - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v2 - name: NuGet Setup Cache uses: actions/cache@v4 From f44b761f3b6731e2c20b56f10515962178ed958c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 27 Feb 2024 11:09:36 +1000 Subject: [PATCH 074/220] Add fixes 2668, 2676, and 2677 to main (#2678) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prevent underflow * Fix file name * Revert "Fix file name" This reverts commit 15619ecd88bc67b6bf4a002094ba241836f8f10a. * Update AlphaDecoder.cs * Use a smarter approach to determine the transparent index * Fix casing * Update src/ImageSharp/Formats/Webp/AlphaDecoder.cs Co-authored-by: Günther Foidl * Update Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png * Normalize Color API * Aggressive inlining --------- Co-authored-by: Günther Foidl --- src/ImageSharp/Color/Color.cs | 93 +++++++++---------- .../Formats/Gif/GifFrameMetadata.cs | 4 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 20 +++- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 9 +- .../Color/ColorTests.CastTo.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 17 ++++ .../Formats/WebP/WebpDecoderTests.cs | 11 +++ tests/ImageSharp.Tests/TestImages.cs | 4 + ...antized_Encode_Alpha_Rgba32_Issue_2668.png | 3 + tests/Images/Input/Png/issues/Issue_2668.png | 3 + tests/Images/Input/Webp/issues/Issue2670.webp | 3 + 11 files changed, 112 insertions(+), 57 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png create mode 100644 tests/Images/Input/Png/issues/Issue_2668.png create mode 100644 tests/Images/Input/Webp/issues/Issue2670.webp diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 82ecab390..8f54680ec 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -25,7 +25,7 @@ public readonly partial struct Color : IEquatable /// Initializes a new instance of the struct. /// /// The containing the color information. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private Color(Vector4 vector) { this.data = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); @@ -36,28 +36,13 @@ public readonly partial struct Color : IEquatable /// Initializes a new instance of the struct. /// /// The pixel containing color information. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private Color(IPixel pixel) { this.boxedHighPrecisionPixel = pixel; this.data = default; } - /// - /// Converts a to . - /// - /// The . - /// The . - public static explicit operator Vector4(Color color) => color.ToScaledVector4(); - - /// - /// Converts an to . - /// - /// The . - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static explicit operator Color(Vector4 source) => new(source); - /// /// Checks whether two structures are equal. /// @@ -67,7 +52,7 @@ public readonly partial struct Color : IEquatable /// True if the parameter is equal to the parameter; /// otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Color left, Color right) => left.Equals(right); /// @@ -79,36 +64,44 @@ public readonly partial struct Color : IEquatable /// True if the parameter is not equal to the parameter; /// otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Color left, Color right) => !left.Equals(right); /// /// Creates a from the given . /// - /// The pixel to convert from. + /// The pixel to convert from. /// The pixel format. /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Color FromPixel(TPixel pixel) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Color FromPixel(TPixel source) where TPixel : unmanaged, IPixel { // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { - return new(pixel.ToScaledVector4()); + return new(source.ToScaledVector4()); } - return new(pixel); + return new(source); } + /// + /// Creates a from a generic scaled . + /// + /// The vector to load the pixel from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Color FromScaledVector(Vector4 source) => new(source); + /// /// Bulk converts a span of a specified type to a span of . /// /// The pixel type to convert to. /// The source pixel span. /// The destination color span. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromPixel(ReadOnlySpan source, Span destination) where TPixel : unmanaged, IPixel { @@ -120,7 +113,7 @@ public readonly partial struct Color : IEquatable { for (int i = 0; i < destination.Length; i++) { - destination[i] = new(source[i].ToScaledVector4()); + destination[i] = FromScaledVector(source[i].ToScaledVector4()); } } else @@ -143,7 +136,7 @@ public readonly partial struct Color : IEquatable /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color ParseHex(string hex) { Rgba32 rgba = Rgba32.ParseHex(hex); @@ -162,7 +155,7 @@ public readonly partial struct Color : IEquatable /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParseHex(string hex, out Color result) { result = default; @@ -236,16 +229,16 @@ public readonly partial struct Color : IEquatable /// The color having it's alpha channel altered. public Color WithAlpha(float alpha) { - Vector4 v = (Vector4)this; + Vector4 v = this.ToScaledVector4(); v.W = alpha; - return new Color(v); + return FromScaledVector(v); } /// /// Gets the hexadecimal representation of the color instance in rrggbbaa form. /// /// A hexadecimal string representation of the value. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToHex() { if (this.boxedHighPrecisionPixel is not null) @@ -263,8 +256,8 @@ public readonly partial struct Color : IEquatable /// Converts the color instance to a specified type. /// /// The pixel type to convert to. - /// The pixel value. - [MethodImpl(InliningOptions.ShortMethod)] + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TPixel ToPixel() where TPixel : unmanaged, IPixel { @@ -281,13 +274,30 @@ public readonly partial struct Color : IEquatable return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); } + /// + /// Expands the color into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() + { + if (this.boxedHighPrecisionPixel is null) + { + return this.data; + } + + return this.boxedHighPrecisionPixel.ToScaledVector4(); + } + /// /// Bulk converts a span of to a span of a specified type. /// /// The pixel type to convert to. /// The source color span. /// The destination pixel span. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToPixel(ReadOnlySpan source, Span destination) where TPixel : unmanaged, IPixel { @@ -301,7 +311,7 @@ public readonly partial struct Color : IEquatable } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Color other) { if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null) @@ -316,7 +326,7 @@ public readonly partial struct Color : IEquatable public override bool Equals(object? obj) => obj is Color other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { if (this.boxedHighPrecisionPixel is null) @@ -326,15 +336,4 @@ public readonly partial struct Color : IEquatable return this.boxedHighPrecisionPixel.GetHashCode(); } - - [MethodImpl(InliningOptions.ShortMethod)] - private Vector4 ToScaledVector4() - { - if (this.boxedHighPrecisionPixel is null) - { - return this.data; - } - - return this.boxedHighPrecisionPixel.ToScaledVector4(); - } } diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs index f8734bb5a..6598def2a 100644 --- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs @@ -82,13 +82,13 @@ public class GifFrameMetadata : IDeepCloneable { // TODO: v4 How do I link the parent metadata to the frame metadata to get the global color table? int index = -1; - float background = 1f; + const float background = 1f; if (metadata.ColorTable.HasValue) { ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; for (int i = 0; i < colorTable.Length; i++) { - Vector4 vector = (Vector4)colorTable[i]; + Vector4 vector = colorTable[i].ToScaledVector4(); if (vector.W < background) { index = i; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 993f53269..113fef595 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Buffers.Binary; using System.IO.Hashing; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; @@ -1559,7 +1560,24 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { // We can use the color data from the decoded metadata here. // We avoid dithering by default to preserve the original colors. - this.derivedTransparencyIndex = metadata.ColorTable.Value.Span.IndexOf(Color.Transparent); + ReadOnlySpan palette = metadata.ColorTable.Value.Span; + + // Certain operations perform alpha premultiplication, which can cause the color to change so we + // must search for the transparency index in the palette. + // Transparent pixels are much more likely to be found at the end of a palette. + int index = -1; + for (int i = palette.Length - 1; i >= 0; i--) + { + Vector4 instance = palette[i].ToScaledVector4(); + if (instance.W == 0f) + { + index = i; + break; + } + } + + this.derivedTransparencyIndex = index; + this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null }, this.derivedTransparencyIndex); } else diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 63e654135..63571617f 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -311,18 +311,15 @@ internal class AlphaDecoder : IDisposable private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { - if (Sse2.IsSupported) + // TODO: Investigate AdvSimd support for this method. + if (Sse2.IsSupported && width >= 9) { dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0])); - if (width <= 1) - { - return; - } - nuint i; Vector128 last = Vector128.Zero.WithElement(0, dst[0]); ref byte srcRef = ref MemoryMarshal.GetReference(input); ref byte dstRef = ref MemoryMarshal.GetReference(dst); + for (i = 1; i <= (uint)width - 8; i += 8) { Vector128 a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0); diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs index 14a5a44f1..4247345c7 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -105,7 +105,7 @@ public partial class ColorTests public void Vector4Constructor() { // Act: - Color color = (Color)Vector4.One; + Color color = Color.FromScaledVector(Vector4.One); // Assert: Assert.Equal(new RgbaVector(1, 1, 1, 1), color.ToPixel()); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 0fdd49630..a70fb86df 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -679,6 +680,22 @@ public partial class PngEncoderTests encoded.CompareToReferenceOutput(ImageComparer.Exact, provider); } + // https://github.com/SixLabors/ImageSharp/issues/2469 + [Theory] + [WithFile(TestImages.Png.Issue2668, PixelTypes.Rgba32)] + public void Issue2668_Quantized_Encode_Alpha(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + image.Mutate(x => x.Resize(100, 100)); + + PngEncoder encoder = new() { BitDepth = PngBitDepth.Bit8, ColorType = PngColorType.Palette }; + + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder); + using Image encoded = Image.Load(actualOutputFile); + encoded.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + private static void TestPngEncoderCore( TestImageProvider provider, PngColorType pngColorType, diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index c38b13075..0dda304b6 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -439,6 +439,17 @@ public class WebpDecoderTests image.CompareToOriginal(provider, ReferenceDecoder); } + // https://github.com/SixLabors/ImageSharp/issues/2670 + [Theory] + [WithFile(Lossy.Issue2670, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2670(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(WebpDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); + } + [Theory] [WithFile(Lossless.LossLessCorruptImage3, PixelTypes.Rgba32)] public void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 933fcc4fa..022c3654a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -151,6 +151,9 @@ public static class TestImages // Issue 2447: https://github.com/SixLabors/ImageSharp/issues/2447 public const string Issue2447 = "Png/issues/issue_2447.png"; + // Issue 2668: https://github.com/SixLabors/ImageSharp/issues/2668 + public const string Issue2668 = "Png/issues/Issue_2668.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; @@ -806,6 +809,7 @@ public static class TestImages public const string Issue1594 = "Webp/issues/Issue1594.webp"; public const string Issue2243 = "Webp/issues/Issue2243.webp"; public const string Issue2257 = "Webp/issues/Issue2257.webp"; + public const string Issue2670 = "Webp/issues/Issue2670.webp"; } } diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png new file mode 100644 index 000000000..7af5391f7 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f934af128b85b9e8f557d71ac8b1f1473a0922d0754fc0c4ece0d0e3d8d94c39 +size 7702 diff --git a/tests/Images/Input/Png/issues/Issue_2668.png b/tests/Images/Input/Png/issues/Issue_2668.png new file mode 100644 index 000000000..2ca8c4617 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2668.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8e5b2b933fd8fefd161f1d22970cb60247fd2d93b6c07b8b9ee1fdbc2241a3c +size 390225 diff --git a/tests/Images/Input/Webp/issues/Issue2670.webp b/tests/Images/Input/Webp/issues/Issue2670.webp new file mode 100644 index 000000000..4dd124898 --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2670.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23ad5eb449f693af68e51dd108a6b9847a8eb48b82ca5b848395a54c2e0be08f +size 152 From 8af9a8068e4b4f54a0a33438fc9937609f28ed79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Mutnia=C5=84ski?= Date: Wed, 28 Feb 2024 11:01:01 +0100 Subject: [PATCH 075/220] PR comments changes --- .../Formats/Jpeg/JpegDecoderCore.cs | 11 ++-- .../Formats/Jpeg/JpegEncoderCore.cs | 53 ++++++++++++------- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 23 ++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index e43c20a7d..cf5e449e7 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -523,13 +523,16 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// The remaining bytes in the segment block. private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize) { - Span temp = new byte[markerContentByteSize]; + char[] temp = new char[markerContentByteSize]; JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); - stream.Read(temp); - string comment = Encoding.ASCII.GetString(temp); + for (int i = 0; i < markerContentByteSize; i++) + { + int read = stream.ReadByte(); + temp[i] = (char)read; + } - metadata.Comments.Add(JpegComData.FromString(comment)); + metadata.Comments.Add(new JpegComData(temp)); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index a55435c53..4ef4cea2d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -3,6 +3,7 @@ #nullable disable using System.Buffers.Binary; +using System.Collections; using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -184,39 +185,55 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals return; } - for (int i = 0; i < metadata.Comments.Count; i++) + // We don't want to modify original metadata + List comments = new(metadata.Comments); + + int totalPayloadLength = 0; + for (int i = 0; i < comments.Count; i++) { - string comment = metadata.Comments[i].ToString(); + JpegComData comment = comments[i]; + ReadOnlyMemory currentComment = comment.Value; - if (comment.Length > maxCommentLength) + if (comment.Value.Length > maxCommentLength) { - string splitComment = comment.Substring(maxCommentLength, comment.Length - maxCommentLength); - metadata.Comments.Insert(i + 1, JpegComData.FromString(splitComment)); + ReadOnlyMemory splitComment = + currentComment.Slice(maxCommentLength, currentComment.Length - maxCommentLength); + comments.Insert(i + 1, new JpegComData(splitComment)); // We don't want to keep the extra bytes - comment = comment.Substring(0, maxCommentLength); + comments[i] = new JpegComData(currentComment.Slice(0, maxCommentLength)); } - int commentLength = comment.Length + 4; + totalPayloadLength += comment.Value.Length + 4; + } + + Span payload = new byte[totalPayloadLength]; + int currentCommentStartingIndex = 0; - Span commentSpan = new byte[commentLength]; - Span markers = commentSpan.Slice(0, 2); - Span payloadSize = commentSpan.Slice(2, 2); - Span payload = commentSpan.Slice(4, comment.Length); + for (int i = 0; i < comments.Count; i++) + { + ReadOnlyMemory comment = comments[i].Value; // Beginning of comment ff fe - markers[0] = JpegConstants.Markers.XFF; - markers[1] = JpegConstants.Markers.COM; + payload[currentCommentStartingIndex] = JpegConstants.Markers.XFF; + payload[currentCommentStartingIndex + 1] = JpegConstants.Markers.COM; // Write payload size - int comWithoutMarker = commentLength - 2; - payloadSize[0] = (byte)((comWithoutMarker >> 8) & 0xFF); - payloadSize[1] = (byte)(comWithoutMarker & 0xFF); + int comWithoutMarker = comment.Length + 2; + payload[currentCommentStartingIndex + 2] = (byte)((comWithoutMarker >> 8) & 0xFF); + payload[currentCommentStartingIndex + 3] = (byte)(comWithoutMarker & 0xFF); - Encoding.ASCII.GetBytes(comment, payload); + char[] commentChars = comment.ToArray(); + for (int j = 0; j < commentChars.Length; j++) + { + // Initial 4 bytes are always reserved + payload[4 + currentCommentStartingIndex + j] = (byte)commentChars[j]; + } - this.outputStream.Write(commentSpan, 0, commentSpan.Length); + currentCommentStartingIndex += comment.Length + 4; } + + this.outputStream.Write(payload, 0, payload.Length); } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index e49afedbb..f0593b462 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -198,6 +198,29 @@ public partial class JpegEncoderTests Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); } + [Fact] + public void Encode_SaveTooLongComment() + { + // arrange + string longString = new('c', 65534); + using var input = new Image(1, 1); + JpegMetadata meta = input.Metadata.GetJpegMetadata(); + using var memStream = new MemoryStream(); + + // act + meta.Comments.Add(JpegComData.FromString(longString)); + input.Save(memStream, JpegEncoder); + + // assert + memStream.Position = 0; + using Image output = Image.Load(memStream); + JpegMetadata actual = output.Metadata.GetJpegMetadata(); + Assert.NotEmpty(actual.Comments); + Assert.Equal(2, actual.Comments.Count); + Assert.Equal(longString[..65533], actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal("c", actual.Comments.ElementAtOrDefault(1).ToString()); + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegEncodingColor.Luminance)] [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] From fac508c4cba7fb6f58c430f17ad8d6074d53e713 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 7 Mar 2024 17:22:28 +1000 Subject: [PATCH 076/220] Can translate between profiles. --- src/ImageSharp/ColorProfiles/CieConstants.cs | 21 +++ src/ImageSharp/ColorProfiles/CieLab.cs | 177 ++++++++++++++++++ src/ImageSharp/ColorProfiles/CieXyz.cs | 124 ++++++++++++ .../ColorProfiles/ColorConversionOptions.cs | 51 +++++ .../ColorProfiles/ColorProfileConverter.cs | 30 +++ ...rProfileConverterExtensionsCieLabCieXyz.cs | 52 +++++ ...rProfileConverterExtensionsCieXyzCieXyz.cs | 52 +++++ .../IColorProfile{TSelf,TProfileSpace}.cs | 47 +++++ .../ColorProfiles/IProfileConnectingSpace.cs | 18 ++ src/ImageSharp/ColorProfiles/Illuminants.cs | 71 +++++++ src/ImageSharp/ColorProfiles/Lms.cs | 136 ++++++++++++++ .../ColorProfiles/LmsAdaptationMatrix.cs | 132 +++++++++++++ .../VonKriesChromaticAdaptation.cs | 90 +++++++++ .../VonKriesChromaticAdaptation.cs | 2 +- .../ApproximateColorProfileComparer.cs | 42 +++++ .../CieXyzAndCieLabConversionTest.cs | 86 +++++++++ .../CieXyzAndLmsConversionTest.cs | 81 ++++++++ .../ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 18 files changed, 1212 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieConstants.cs create mode 100644 src/ImageSharp/ColorProfiles/CieLab.cs create mode 100644 src/ImageSharp/ColorProfiles/CieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorConversionOptions.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverter.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs create mode 100644 src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/Illuminants.cs create mode 100644 src/ImageSharp/ColorProfiles/Lms.cs create mode 100644 src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs create mode 100644 src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieConstants.cs b/src/ImageSharp/ColorProfiles/CieConstants.cs new file mode 100644 index 000000000..f4b74eaa1 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieConstants.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Constants use for Cie conversion calculations +/// +/// +internal static class CieConstants +{ + /// + /// 216F / 24389F + /// + public const float Epsilon = 0.008856452F; + + /// + /// 24389F / 27F + /// + public const float Kappa = 903.2963F; +} diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs new file mode 100644 index 000000000..c06fe765c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -0,0 +1,177 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a CIE L*a*b* 1976 color. +/// +/// +public readonly struct CieLab : IProfileConnectingSpace +{ + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLab(float l, float a, float b) + : this(new Vector3(l, a, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, a, b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLab(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A { get; } + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLab left, CieLab right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLab other) => + this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html + CieXyz whitePoint = options.TargetWhitePoint; + float wx = whitePoint.X, wy = whitePoint.Y, wz = whitePoint.Z; + + float xr = source.X / wx, yr = source.Y / wy, zr = source.Z / wz; + + const float inv116 = 1 / 116F; + + float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) * inv116; + float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) * inv116; + float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) * inv116; + + float l = (116F * fy) - 16F; + float a = 500F * (fx - fy); + float b = 200F * (fy - fz); + + return new CieLab(l, a, b); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + float l = this.L, a = this.A, b = this.B; + float fy = (l + 16) / 116F; + float fx = (a / 500F) + fy; + float fz = fy - (b / 200F); + + float fx3 = Numerics.Pow3(fx); + float fz3 = Numerics.Pow3(fz); + + float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; + float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa; + float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa; + + CieXyz whitePoint = options.WhitePoint; + Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z); + + // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) + Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); + + return new(xyzr * wxyz); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLab lab = source[i]; + destination[i] = lab.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs new file mode 100644 index 000000000..b22ab16f9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -0,0 +1,124 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CIE XYZ 1931 color +/// +/// +public readonly struct CieXyz : IProfileConnectingSpace +{ + /// + /// Initializes a new instance of the struct. + /// + /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative + /// The y luminance component. + /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieXyz(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the x, y, z components. + public CieXyz(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Z = vector.Z; + } + + /// + /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. + /// A value usually ranging between 0 and 1. + /// + public float X { get; } + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. + /// + public float Y { get; } + + /// + /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response. + /// A value usually ranging between 0 and 1. + /// + public float Z { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.X, this.Y, this.Z); + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieXyz other) + => this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Z.Equals(other.Z); + + /// + public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + => new(source.X, source.Y, source.Z); + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + source.CopyTo(destination[..source.Length]); + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + => new(this.X, this.Y, this.Z); + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + source.CopyTo(destination[..source.Length]); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs new file mode 100644 index 000000000..de32aa54d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Provides options for color profile conversion. +/// +public class ColorConversionOptions +{ + private Matrix4x4 adaptationMatrix; + + /// + /// Initializes a new instance of the class. + /// + public ColorConversionOptions() => this.AdaptationMatrix = LmsAdaptationMatrix.Bradford; + + /// + /// Gets the memory allocator. + /// + public MemoryAllocator MemoryAllocator { get; init; } = MemoryAllocator.Default; + + /// + /// Gets the source white point used for chromatic adaptation in conversions from/to XYZ color space. + /// + public CieXyz WhitePoint { get; init; } = Illuminants.D50; + + /// + /// Gets the destination white point used for chromatic adaptation in conversions from/to XYZ color space. + /// + public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + + /// + /// Gets the transformation matrix used in conversion to perform chromatic adaptation. + /// + public Matrix4x4 AdaptationMatrix + { + get => this.adaptationMatrix; + init + { + this.adaptationMatrix = value; + Matrix4x4.Invert(value, out Matrix4x4 inverted); + this.InverseAdaptationMatrix = inverted; + } + } + + internal Matrix4x4 InverseAdaptationMatrix { get; private set; } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs new file mode 100644 index 000000000..af9ab0af8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Allows the conversion of color profiles. +/// +public class ColorProfileConverter +{ + /// + /// Initializes a new instance of the class. + /// + public ColorProfileConverter() + : this(new()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The color profile conversion options. + public ColorProfileConverter(ColorConversionOptions options) + => this.Options = options; + + /// + /// Gets the color profile conversion options. + /// + public ColorConversionOptions Options { get; } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs new file mode 100644 index 000000000..481280b85 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFrom = source.ToProfileConnectingSpace(options); + + // Convert between PCS + CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsTo); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length * 2); + Span pcsTo = pcsToOwner.GetSpan()[..source.Length]; + CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs new file mode 100644 index 000000000..ebe07003e --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieXyz.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs b/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs new file mode 100644 index 000000000..e1d4cae35 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Defines the contract for all color profiles. +/// +/// The type of color profile. +/// The type of color profile connecting space. +public interface IColorProfile : IEquatable + where TSelf : IColorProfile + where TProfileSpace : struct, IProfileConnectingSpace +{ + /// + /// Converts the color to the profile connection space. + /// + /// The color profile conversion options. + /// The . + public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); + +#pragma warning disable CA1000 // Do not declare static members on generic types + /// + /// Converts the color from the profile connection space. + /// + /// The color profile conversion options. + /// The color profile connecting space. + /// The . + public static abstract TSelf FromProfileConnectingSpace(ColorConversionOptions options, in TProfileSpace source); + + /// + /// Converts the span of colors to the profile connection space. + /// + /// The color profile conversion options. + /// The color span to convert from. + /// The color profile span to write the results to. + public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); + + /// + /// Converts the span of colors from the profile connection space. + /// + /// The color profile conversion options. + /// The color profile span to convert from. + /// The color span to write the results to. + public static abstract void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); +#pragma warning restore CA1000 // Do not declare static members on generic types +} diff --git a/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs b/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs new file mode 100644 index 000000000..2ac736f44 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Defines the contract for all color profile connection spaces. +/// +public interface IProfileConnectingSpace; + +/// +/// Defines the contract for all color profile connection spaces. +/// +/// The type of color profile. +/// The type of color profile connecting space. +public interface IProfileConnectingSpace : IColorProfile, IProfileConnectingSpace + where TSelf : struct, IColorProfile, IProfileConnectingSpace + where TProfileSpace : struct, IProfileConnectingSpace; diff --git a/src/ImageSharp/ColorProfiles/Illuminants.cs b/src/ImageSharp/ColorProfiles/Illuminants.cs new file mode 100644 index 000000000..0295dc484 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Illuminants.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// The well known standard illuminants. +/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting +/// +/// +/// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html +///
    +/// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant +///
    +public static class Illuminants +{ + /// + /// Incandescent / Tungsten + /// + public static readonly CieXyz A = new(1.09850F, 1F, 0.35585F); + + /// + /// Direct sunlight at noon (obsoleteF) + /// + public static readonly CieXyz B = new(0.99072F, 1F, 0.85223F); + + /// + /// Average / North sky Daylight (obsoleteF) + /// + public static readonly CieXyz C = new(0.98074F, 1F, 1.18232F); + + /// + /// Horizon Light. ICC profile PCS + /// + public static readonly CieXyz D50 = new(0.96422F, 1F, 0.82521F); + + /// + /// Mid-morning / Mid-afternoon Daylight + /// + public static readonly CieXyz D55 = new(0.95682F, 1F, 0.92149F); + + /// + /// Noon Daylight: TelevisionF, sRGB color space + /// + public static readonly CieXyz D65 = new(0.95047F, 1F, 1.08883F); + + /// + /// North sky Daylight + /// + public static readonly CieXyz D75 = new(0.94972F, 1F, 1.22638F); + + /// + /// Equal energy + /// + public static readonly CieXyz E = new(1F, 1F, 1F); + + /// + /// Cool White Fluorescent + /// + public static readonly CieXyz F2 = new(0.99186F, 1F, 0.67393F); + + /// + /// D65 simulatorF, Daylight simulator + /// + public static readonly CieXyz F7 = new(0.95041F, 1F, 1.08747F); + + /// + /// Philips TL84F, Ultralume 40 + /// + public static readonly CieXyz F11 = new(1.00962F, 1F, 0.64350F); +} diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs new file mode 100644 index 000000000..e11c1ca87 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal readonly struct Lms : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// L represents the responsivity at long wavelengths. + /// M represents the responsivity at medium wavelengths. + /// S represents the responsivity at short wavelengths. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Lms(float l, float m, float s) + : this(new Vector3(l, m, s)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, m, s components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Lms(Vector3 vector) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.M = vector.Y; + this.S = vector.Z; + } + + /// + /// Gets the L long component. + /// A value usually ranging between -1 and 1. + /// + public readonly float L { get; } + + /// + /// Gets the M medium component. + /// A value usually ranging between -1 and 1. + /// + public readonly float M { get; } + + /// + /// Gets the S short component. + /// A value usually ranging between -1 and 1. + /// + public readonly float S { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Lms left, Lms right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Lms left, Lms right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.L, this.M, this.S); + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); + + /// + public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Lms other) + => this.L.Equals(other.L) + && this.M.Equals(other.M) + && this.S.Equals(other.S); + + /// + public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + Vector3 vector = Vector3.Transform(source.ToVector3(), options.AdaptationMatrix); + return new Lms(vector); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + Vector3 vector = Vector3.Transform(this.ToVector3(), options.InverseAdaptationMatrix); + return new CieXyz(vector); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + Lms lms = source[i]; + destination[i] = lms.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs new file mode 100644 index 000000000..70bc5aef2 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs @@ -0,0 +1,132 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Matrices used for transformation from to , defining the cone response domain. +/// +/// +/// Matrix data obtained from: +/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization +/// S. Bianco, R. Schettini +/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy +/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf +/// +public static class LmsAdaptationMatrix +{ + /// + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) + /// + public static readonly Matrix4x4 VonKriesHPEAdjusted + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 0.40024F, + M12 = 0.7076F, + M13 = -0.08081F, + M21 = -0.2263F, + M22 = 1.16532F, + M23 = 0.0457F, + M31 = 0, + M32 = 0, + M33 = 0.91822F, + M44 = 1F // Important for inverse transforms. + }); + + /// + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) + /// + public static readonly Matrix4x4 VonKriesHPE + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 0.3897F, + M12 = 0.6890F, + M13 = -0.0787F, + M21 = -0.2298F, + M22 = 1.1834F, + M23 = 0.0464F, + M31 = 0, + M32 = 0, + M33 = 1F, + M44 = 1F + }); + + /// + /// XYZ scaling chromatic adaptation transform matrix + /// + public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); + + /// + /// Bradford chromatic adaptation transform matrix (used in CMCCAT97) + /// + public static readonly Matrix4x4 Bradford + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 0.8951F, + M12 = 0.2664F, + M13 = -0.1614F, + M21 = -0.7502F, + M22 = 1.7135F, + M23 = 0.0367F, + M31 = 0.0389F, + M32 = -0.0685F, + M33 = 1.0296F, + M44 = 1F + }); + + /// + /// Spectral sharpening and the Bradford transform + /// + public static readonly Matrix4x4 BradfordSharp + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 1.2694F, + M12 = -0.0988F, + M13 = -0.1706F, + M21 = -0.8364F, + M22 = 1.8006F, + M23 = 0.0357F, + M31 = 0.0297F, + M32 = -0.0315F, + M33 = 1.0018F, + M44 = 1F + }); + + /// + /// CMCCAT2000 (fitted from all available color data sets) + /// + public static readonly Matrix4x4 CMCCAT2000 + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 0.7982F, + M12 = 0.3389F, + M13 = -0.1371F, + M21 = -0.5918F, + M22 = 1.5512F, + M23 = 0.0406F, + M31 = 0.0008F, + M32 = 0.239F, + M33 = 0.9753F, + M44 = 1F + }); + + /// + /// CAT02 (optimized for minimizing CIELAB differences) + /// + public static readonly Matrix4x4 CAT02 + = Matrix4x4.Transpose(new Matrix4x4 + { + M11 = 0.7328F, + M12 = 0.4296F, + M13 = -0.1624F, + M21 = -0.7036F, + M22 = 1.6975F, + M23 = 0.0061F, + M31 = 0.0030F, + M32 = 0.0136F, + M33 = 0.9834F, + M44 = 1F + }); +} diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs new file mode 100644 index 000000000..0505395de --- /dev/null +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -0,0 +1,90 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Implementation of the Von Kries chromatic adaptation model. +/// +/// +/// Transformation described here: +/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html +/// +public static class VonKriesChromaticAdaptation +{ + /// + /// Performs a linear transformation of a source color in to the destination color. + /// + /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). + /// The color profile conversion options. + /// The source color. + /// The + public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + { + CieXyz sourceWhitePoint = options.WhitePoint; + CieXyz destinationWhitePoint = options.TargetWhitePoint; + + if (sourceWhitePoint.Equals(destinationWhitePoint)) + { + return new(source.X, source.Y, source.Z); + } + + Matrix4x4 matrix = options.AdaptationMatrix; + + Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); + + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + return new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); + } + + /// + /// Performs a bulk linear transformation of a source color in to the destination color. + /// + /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). + /// The color profile conversion options. + /// The span to the source colors. + /// The span to the destination colors. + public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + CieXyz sourceWhitePoint = options.WhitePoint; + CieXyz destinationWhitePoint = options.TargetWhitePoint; + + if (sourceWhitePoint.Equals(destinationWhitePoint)) + { + source.CopyTo(destination[..count]); + return; + } + + Matrix4x4 matrix = options.AdaptationMatrix; + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + + ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); + ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceBase, i); + ref CieXyz dp = ref Unsafe.Add(ref destinationBase, i); + + Vector3 sourceColorLms = Vector3.Transform(sp.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); + dp = new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); + } + } +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs index 97e9cee81..8f33e59f5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion; /// -/// Implementation of the von Kries chromatic adaptation model. +/// Implementation of the Von Kries chromatic adaptation model. /// /// /// Transformation described here: diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs new file mode 100644 index 000000000..74fa13216 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Allows the approximate comparison of color profile component values. +/// +internal readonly struct ApproximateColorProfileComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer +{ + private readonly float epsilon; + + /// + /// Initializes a new instance of the struct. + /// + /// The comparison error difference epsilon to use. + public ApproximateColorProfileComparer(float epsilon = 1f) => this.epsilon = epsilon; + + public bool Equals(CieLab x, CieLab y) => this.Equals(x.L, y.L) && this.Equals(x.A, y.A) && this.Equals(x.B, y.B); + + public bool Equals(CieXyz x, CieXyz y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z); + + public bool Equals(Lms x, Lms y) => this.Equals(x.L, y.L) && this.Equals(x.M, y.M) && this.Equals(x.S, y.S); + + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] Lms obj) => obj.GetHashCode(); + + private bool Equals(float x, float y) + { + float d = x - y; + return d >= -this.epsilon && d <= this.epsilon; + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs new file mode 100644 index 000000000..da2bcbc87 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieLabConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 431.0345, 0, 0.95047, 0, 0)] + [InlineData(100, -431.0345, 172.4138, 0, 1, 0)] + [InlineData(0, 0, -172.4138, 0, 0, 1.08883)] + [InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] + [InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] + [InlineData(10, -400, 20, 0, 0.011260, 0)] + public void Convert_Lab_to_Xyz(float l, float a, float b, float x, float y, float z) + { + // Arrange + CieLab input = new(l, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.95047, 0, 0, 0, 431.0345, 0)] + [InlineData(0, 1, 0, 100, -431.0345, 172.4138)] + [InlineData(0, 0, 1.08883, 0, 0, -172.4138)] + [InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] + public void Convert_Xyz_to_Lab(float x, float y, float z, float l, float a, float b) + { + // Arrange + CieXyz input = new(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + CieLab expected = new(l, a, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs new file mode 100644 index 000000000..60938991e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using original colorful library. +/// +public class CieXyzAndLmsConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0.941428535, 1.040417467, 1.089532651, 0.95047, 1, 1.08883)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.850765697, -0.713042594, 0.036973283, 0.95047, 0, 0)] + [InlineData(0.2664, 1.7135, -0.0685, 0, 1, 0)] + [InlineData(-0.175737162, 0.039960061, 1.121059368, 0, 0, 1.08883)] + [InlineData(0.2262677362, 0.0961411609, 0.0484570397, 0.216938, 0.150041, 0.048850)] + public void Convert_Lms_to_CieXyz(float l, float m, float s, float x, float y, float z) + { + // Arrange + Lms input = new(l, m, s); + ColorProfileConverter converter = new(); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.95047, 1, 1.08883, 0.941428535, 1.040417467, 1.089532651)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.95047, 0, 0, 0.850765697, -0.713042594, 0.036973283)] + [InlineData(0, 1, 0, 0.2664, 1.7135, -0.0685)] + [InlineData(0, 0, 1.08883, -0.175737162, 0.039960061, 1.121059368)] + [InlineData(0.216938, 0.150041, 0.048850, 0.2262677362, 0.0961411609, 0.0484570397)] + public void Convert_CieXyz_to_Lms(float x, float y, float z, float l, float m, float s) + { + // Arrange + CieXyz input = new(x, y, z); + ColorProfileConverter converter = new(); + Lms expected = new(l, m, s); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index a389c8ab8..41e6e525f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -39,7 +39,7 @@ Do not update or consolidate BenchmarkDotNet. https://github.com/dotnet/arcade/issues/8483 --> - + From 0ac9f3db4daccc1b768d8f6b795a0f4da2f780f5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 7 Mar 2024 17:35:04 +1000 Subject: [PATCH 077/220] Update AlphaDecoder.cs --- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 63571617f..eccd9ede8 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -6,7 +6,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Memory; @@ -311,8 +313,7 @@ internal class AlphaDecoder : IDisposable private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { - // TODO: Investigate AdvSimd support for this method. - if (Sse2.IsSupported && width >= 9) + if ((Sse2.IsSupported || AdvSimd.IsSupported) && width >= 9) { dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0])); nuint i; @@ -323,17 +324,17 @@ internal class AlphaDecoder : IDisposable for (i = 1; i <= (uint)width - 8; i += 8) { Vector128 a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0); - Vector128 a1 = Sse2.Add(a0.AsByte(), last.AsByte()); - Vector128 a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1); - Vector128 a3 = Sse2.Add(a1, a2); - Vector128 a4 = Sse2.ShiftLeftLogical128BitLane(a3, 2); - Vector128 a5 = Sse2.Add(a3, a4); - Vector128 a6 = Sse2.ShiftLeftLogical128BitLane(a5, 4); - Vector128 a7 = Sse2.Add(a5, a6); + Vector128 a1 = a0.AsByte() + last.AsByte(); + Vector128 a2 = Vector128Utilities.ShiftLeftBytesInVector(a1, 1); + Vector128 a3 = a1 + a2; + Vector128 a4 = Vector128Utilities.ShiftLeftBytesInVector(a3, 2); + Vector128 a5 = a3 + a4; + Vector128 a6 = Vector128Utilities.ShiftLeftBytesInVector(a5, 4); + Vector128 a7 = a5 + a6; ref byte outputRef = ref Unsafe.Add(ref dstRef, i); Unsafe.As>(ref outputRef) = a7.GetLower(); - last = Sse2.ShiftRightLogical(a7.AsInt64(), 56).AsInt32(); + last = Vector128.ShiftRightLogical(a7.AsInt64(), 56).AsInt32(); } for (; i < (uint)width; ++i) From 8bf86986a45d2e36fbd74a68bbd7dea65195ad90 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 7 Mar 2024 19:41:24 +1000 Subject: [PATCH 078/220] Update PngDecoderCore.cs --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 194ee1661..6a321a3ba 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -125,7 +125,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// A reusable Crc32 hashing instance. ///
    private readonly Crc32 crc32 = new(); - + + /// /// The maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed. /// private readonly int maxUncompressedLength; From 8bc794af659d0d00ee039482dfbe0ca25f8f661f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Mar 2024 21:50:46 +1000 Subject: [PATCH 079/220] Add CieLch --- src/ImageSharp/ColorProfiles/CieLab.cs | 4 +- src/ImageSharp/ColorProfiles/CieLch.cs | 166 ++++++++++++++++++ ...rProfileConverterExtensionsCieXyzCieLab.cs | 52 ++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieLabAndCieLchConversionTests.cs | 88 ++++++++++ 5 files changed, 313 insertions(+), 4 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieLch.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index c06fe765c..248559f73 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -155,9 +155,7 @@ public readonly struct CieLab : IProfileConnectingSpace CieXyz whitePoint = options.WhitePoint; Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z); - - // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) - Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); + Vector3 xyzr = new(xr, yr, zr); return new(xyzr * wxyz); } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs new file mode 100644 index 000000000..4b48e1c8d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -0,0 +1,166 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. +/// +/// +public readonly struct CieLch : IColorProfile +{ + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + + private static readonly Vector3 Min = new(0, -200, 0); + private static readonly Vector3 Max = new(100, 200, 360); + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The chroma, relative saturation. + /// The hue in degrees. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLch(float l, float c, float h) + : this(new Vector3(l, c, h)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, c, h components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLch(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C { get; } + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. + /// + public readonly float H { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLch left, CieLch right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H); + + /// + public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLch other) + => this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H); + + /// + public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + float l = source.L, a = source.A, b = source.B; + float c = MathF.Sqrt((a * a) + (b * b)); + float hRadians = MathF.Atan2(b, a); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); + + // Wrap the angle round at 360. + hDegrees %= 360; + + // Make sure it's not negative. + while (hDegrees < 0) + { + hDegrees += 360; + } + + return new CieLch(l, c, hDegrees); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLab lab = source[i]; + destination[i] = FromProfileConnectingSpace(options, in lab); + } + } + + /// + public CieLab ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + float l = this.L, c = this.C, hDegrees = this.H; + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); + + float a = c * MathF.Cos(hRadians); + float b = c * MathF.Sin(hRadians); + + return new CieLab(l, a, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLch lch = source[i]; + destination[i] = lch.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs new file mode 100644 index 000000000..949cd6c8e --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 74fa13216..5c2f9afbd 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -12,7 +12,8 @@ namespace SixLabors.ImageSharp.Tests.ColorProfiles; internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -28,12 +29,16 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Lms x, Lms y) => this.Equals(x.L, y.L) && this.Equals(x.M, y.M) && this.Equals(x.S, y.S); + public bool Equals(CieLch x, CieLch y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] Lms obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieLch obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs new file mode 100644 index 000000000..0eaf30b81 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs @@ -0,0 +1,88 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, 50, 180, 100, -50, 0)] + [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] + [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] + [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] + [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] + public void Convert_Lch_to_Lab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + CieLch input = new(l, c, h); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, -50, 0, 100, 50, 180)] + [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] + [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] + [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] + [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] + public void Convert_Lab_to_Lch(float l, float a, float b, float l2, float c, float h) + { + // Arrange + CieLab input = new(l, a, b); + CieLch expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 14ced6f6d2594cba4a54711b134301c96407e841 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 12 Mar 2024 12:02:45 +0100 Subject: [PATCH 080/220] Set YcbcrSubSampling to 1, 1 with jpeg compression and PhotometricInterpretation YCbCr --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 5a5c2b3e5..7172bd6fe 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -628,6 +628,12 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.Rgb; } + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + options.YcbcrSubSampling[0] = 1; + options.YcbcrSubSampling[1] = 1; + } + break; } @@ -642,6 +648,12 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.Rgb; } + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + options.YcbcrSubSampling[0] = 1; + options.YcbcrSubSampling[1] = 1; + } + break; } From 363769d28220ec8117ecee63f7c0a034b17aedbb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 12 Mar 2024 12:21:58 +0100 Subject: [PATCH 081/220] Avoid code duplication --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 7172bd6fe..7b0f439b1 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -620,19 +620,7 @@ internal static class TiffDecoderOptionsParser } options.CompressionType = TiffDecoderCompressionType.OldJpeg; - - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) - { - // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. - options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - options.ColorType = TiffColorType.Rgb; - } - - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) - { - options.YcbcrSubSampling[0] = 1; - options.YcbcrSubSampling[1] = 1; - } + AdjustOptionsYCbCrJpegCompression(options); break; } @@ -640,19 +628,7 @@ internal static class TiffDecoderOptionsParser case TiffCompression.Jpeg: { options.CompressionType = TiffDecoderCompressionType.Jpeg; - - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) - { - // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. - options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - options.ColorType = TiffColorType.Rgb; - } - - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) - { - options.YcbcrSubSampling[0] = 1; - options.YcbcrSubSampling[1] = 1; - } + AdjustOptionsYCbCrJpegCompression(options); break; } @@ -671,6 +647,24 @@ internal static class TiffDecoderOptionsParser } } + private static void AdjustOptionsYCbCrJpegCompression(TiffDecoderCore options) + { + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + options.ColorType = TiffColorType.Rgb; + } + + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + // Some tiff encoder set this to values different from [1, 1]. The jpeg decoder already handles this, + // so we set this always to [1, 1], see: https://github.com/SixLabors/ImageSharp/issues/2679 + options.YcbcrSubSampling[0] = 1; + options.YcbcrSubSampling[1] = 1; + } + } + private static bool IsBiColorCompression(TiffCompression? compression) { if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or From d8484da7399659a78c044545589a4c5ec67bbda0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 13 Mar 2024 15:10:20 +1000 Subject: [PATCH 082/220] Remove allocations and cleanup. --- src/ImageSharp/Formats/Jpeg/JpegComData.cs | 24 ++-- .../Formats/Jpeg/JpegDecoderCore.cs | 9 +- .../Formats/Jpeg/JpegEncoderCore.cs | 77 +++++-------- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 104 ++++++++---------- 4 files changed, 92 insertions(+), 122 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegComData.cs b/src/ImageSharp/Formats/Jpeg/JpegComData.cs index 26ade0217..4e832d903 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegComData.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegComData.cs @@ -1,32 +1,32 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Jpeg; /// -/// Contains JPEG comment +/// Represents a JPEG comment /// public readonly struct JpegComData { - /// - /// Converts string to - /// - /// The comment string. - /// The - public static JpegComData FromString(string value) => new(value.AsMemory()); - /// /// Initializes a new instance of the struct. /// - /// The comment ReadOnlyMemory of chars. + /// The comment buffer. public JpegComData(ReadOnlyMemory value) => this.Value = value; + /// + /// Gets the value. + /// public ReadOnlyMemory Value { get; } /// - /// Converts Value to string + /// Converts string to /// - /// The comment string. + /// The comment string. + /// The + public static JpegComData FromString(string value) => new(value.AsMemory()); + + /// public override string ToString() => this.Value.ToString(); } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index cf5e449e7..906505b76 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -481,6 +480,8 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals break; case JpegConstants.Markers.APP15: + stream.Skip(markerContentByteSize); + break; case JpegConstants.Markers.COM: this.ProcessComMarker(stream, markerContentByteSize); break; @@ -523,16 +524,16 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// The remaining bytes in the segment block. private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize) { - char[] temp = new char[markerContentByteSize]; + char[] chars = new char[markerContentByteSize]; JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); for (int i = 0; i < markerContentByteSize; i++) { int read = stream.ReadByte(); - temp[i] = (char)read; + chars[i] = (char)read; } - metadata.Comments.Add(new JpegComData(temp)); + metadata.Comments.Add(new JpegComData(chars)); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 4ef4cea2d..243bbe051 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -2,9 +2,8 @@ // Licensed under the Six Labors Split License. #nullable disable +using System.Buffers; using System.Buffers.Binary; -using System.Collections; -using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; @@ -27,6 +26,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// private static readonly JpegFrameConfig[] FrameConfigs = CreateFrameConfigs(); + /// + /// The current calling encoder. + /// private readonly JpegEncoder encoder; /// @@ -92,7 +94,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals this.WriteProfiles(metadata, buffer); // Write comments - this.WriteComment(jpegMetadata); + this.WriteComments(image.Configuration, jpegMetadata); // Write the image dimensions. this.WriteStartOfFrame(image.Width, image.Height, frameConfig, buffer); @@ -173,67 +175,48 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals } /// - /// Writes comment + /// Writes the COM tags. /// + /// The configuration. /// The image metadata. - private void WriteComment(JpegMetadata metadata) + private void WriteComments(Configuration configuration, JpegMetadata metadata) { - int maxCommentLength = 65533; - if (metadata.Comments.Count == 0) { return; } - // We don't want to modify original metadata - List comments = new(metadata.Comments); - - int totalPayloadLength = 0; - for (int i = 0; i < comments.Count; i++) + const int maxCommentLength = 65533; + using IMemoryOwner bufferOwner = configuration.MemoryAllocator.Allocate(maxCommentLength); + Span buffer = bufferOwner.Memory.Span; + foreach (JpegComData comment in metadata.Comments) { - JpegComData comment = comments[i]; - ReadOnlyMemory currentComment = comment.Value; - - if (comment.Value.Length > maxCommentLength) + int totalLength = comment.Value.Length; + if (totalLength == 0) { - ReadOnlyMemory splitComment = - currentComment.Slice(maxCommentLength, currentComment.Length - maxCommentLength); - comments.Insert(i + 1, new JpegComData(splitComment)); - - // We don't want to keep the extra bytes - comments[i] = new JpegComData(currentComment.Slice(0, maxCommentLength)); + continue; } - totalPayloadLength += comment.Value.Length + 4; - } - - Span payload = new byte[totalPayloadLength]; - int currentCommentStartingIndex = 0; - - for (int i = 0; i < comments.Count; i++) - { - ReadOnlyMemory comment = comments[i].Value; + // Loop through and split the comment into multiple comments if the comment length + // is greater than the maximum allowed length. + while (totalLength > 0) + { + int currentLength = Math.Min(totalLength, maxCommentLength); - // Beginning of comment ff fe - payload[currentCommentStartingIndex] = JpegConstants.Markers.XFF; - payload[currentCommentStartingIndex + 1] = JpegConstants.Markers.COM; + // Write the marker header. + this.WriteMarkerHeader(JpegConstants.Markers.COM, currentLength + 2, buffer); - // Write payload size - int comWithoutMarker = comment.Length + 2; - payload[currentCommentStartingIndex + 2] = (byte)((comWithoutMarker >> 8) & 0xFF); - payload[currentCommentStartingIndex + 3] = (byte)(comWithoutMarker & 0xFF); + ReadOnlySpan commentValue = comment.Value.Span.Slice(comment.Value.Length - totalLength, currentLength); + for (int i = 0; i < commentValue.Length; i++) + { + buffer[i] = (byte)commentValue[i]; + } - char[] commentChars = comment.ToArray(); - for (int j = 0; j < commentChars.Length; j++) - { - // Initial 4 bytes are always reserved - payload[4 + currentCommentStartingIndex + j] = (byte)commentChars[j]; + // Write the comment. + this.outputStream.Write(buffer, 0, currentLength); + totalLength -= currentLength; } - - currentCommentStartingIndex += comment.Length + 4; } - - this.outputStream.Write(payload, 0, payload.Length); } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index f0593b462..f06fbe963 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -32,19 +32,19 @@ public partial class JpegEncoderTests public void Encode_PreservesIptcProfile() { // arrange - using var input = new Image(1, 1); - var expectedProfile = new IptcProfile(); + using Image input = new(1, 1); + IptcProfile expectedProfile = new(); expectedProfile.SetValue(IptcTag.Country, "ESPAÑA"); expectedProfile.SetValue(IptcTag.City, "unit-test-city"); input.Metadata.IptcProfile = expectedProfile; // act - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); input.Save(memStream, JpegEncoder); // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); IptcProfile actual = output.Metadata.IptcProfile; Assert.NotNull(actual); IEnumerable values = expectedProfile.Values; @@ -55,17 +55,17 @@ public partial class JpegEncoderTests public void Encode_PreservesExifProfile() { // arrange - using var input = new Image(1, 1); + using Image input = new(1, 1); input.Metadata.ExifProfile = new ExifProfile(); input.Metadata.ExifProfile.SetValue(ExifTag.Software, "unit_test"); // act - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); input.Save(memStream, JpegEncoder); // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); ExifProfile actual = output.Metadata.ExifProfile; Assert.NotNull(actual); IReadOnlyList values = input.Metadata.ExifProfile.Values; @@ -76,16 +76,16 @@ public partial class JpegEncoderTests public void Encode_PreservesIccProfile() { // arrange - using var input = new Image(1, 1); + using Image input = new(1, 1); input.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.Profile_Random_Array); // act - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); input.Save(memStream, JpegEncoder); // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); IccProfile actual = output.Metadata.IccProfile; Assert.NotNull(actual); IccProfile values = input.Metadata.IccProfile; @@ -99,12 +99,10 @@ public partial class JpegEncoderTests { Exception ex = Record.Exception(() => { - var encoder = new JpegEncoder(); - using (var stream = new MemoryStream()) - { - using Image image = provider.GetImage(JpegDecoder.Instance); - image.Save(stream, encoder); - } + JpegEncoder encoder = new(); + using MemoryStream stream = new(); + using Image image = provider.GetImage(JpegDecoder.Instance); + image.Save(stream, encoder); }); Assert.Null(ex); @@ -114,23 +112,17 @@ public partial class JpegEncoderTests [MemberData(nameof(RatioFiles))] public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, JpegEncoder); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageMetadata meta = output.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } - } + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, JpegEncoder); + + memStream.Position = 0; + using Image output = Image.Load(memStream); + ImageMetadata meta = output.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -138,20 +130,14 @@ public partial class JpegEncoderTests public void Encode_PreservesQuality(string imagePath, int quality) { TestFile testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, JpegEncoder); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - JpegMetadata meta = output.Metadata.GetJpegMetadata(); - Assert.Equal(quality, meta.Quality); - } - } - } + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, JpegEncoder); + + memStream.Position = 0; + using Image output = Image.Load(memStream); + JpegMetadata meta = output.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); } [Theory] @@ -161,7 +147,7 @@ public partial class JpegEncoderTests { // arrange using Image input = provider.GetImage(JpegDecoder.Instance); - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); // act input.Save(memStream, JpegEncoder); @@ -172,16 +158,16 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(1, actual.Comments.Count); - Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal("TEST COMMENT", actual.Comments[0].ToString()); } [Fact] public void Encode_SavesMultipleComments() { // arrange - using var input = new Image(1, 1); + using Image input = new(1, 1); JpegMetadata meta = input.Metadata.GetJpegMetadata(); - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); // act meta.Comments.Add(JpegComData.FromString("First comment")); @@ -194,8 +180,8 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(2, actual.Comments.Count); - Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString()); - Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); + Assert.Equal(meta.Comments[0].ToString(), actual.Comments[0].ToString()); + Assert.Equal(meta.Comments[1].ToString(), actual.Comments[1].ToString()); } [Fact] @@ -203,9 +189,9 @@ public partial class JpegEncoderTests { // arrange string longString = new('c', 65534); - using var input = new Image(1, 1); + using Image input = new(1, 1); JpegMetadata meta = input.Metadata.GetJpegMetadata(); - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); // act meta.Comments.Add(JpegComData.FromString(longString)); @@ -217,8 +203,8 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); Assert.Equal(2, actual.Comments.Count); - Assert.Equal(longString[..65533], actual.Comments.ElementAtOrDefault(0).ToString()); - Assert.Equal("c", actual.Comments.ElementAtOrDefault(1).ToString()); + Assert.Equal(longString[..65533], actual.Comments[0].ToString()); + Assert.Equal("c", actual.Comments[1].ToString()); } [Theory] @@ -231,14 +217,14 @@ public partial class JpegEncoderTests { // arrange using Image input = provider.GetImage(JpegDecoder.Instance); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); // act input.Save(memoryStream, JpegEncoder); // assert memoryStream.Position = 0; - using var output = Image.Load(memoryStream); + using Image output = Image.Load(memoryStream); JpegMetadata meta = output.Metadata.GetJpegMetadata(); Assert.Equal(expectedColorType, meta.ColorType); } From 382ee7fb2351979a194684707491a88a22a9070b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 19 Mar 2024 00:30:20 +1000 Subject: [PATCH 083/220] Only exit after multiple EOF hits --- .../Jpeg/Components/Decoder/JpegBitReader.cs | 20 +++++++++++++++---- .../Formats/Jpg/JpegDecoderTests.cs | 11 ++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Jpg/issues/Issue2638.jpg | 3 +++ 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/Issue2638.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs index 0877dbc92..c2b0cb6e0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs @@ -22,6 +22,9 @@ internal struct JpegBitReader // Whether there is no more good data to pull from the stream for the current mcu. private bool badData; + // How many times have we hit the eof. + private int eofHitCount; + public JpegBitReader(BufferedReadStream stream) { this.stream = stream; @@ -31,6 +34,7 @@ internal struct JpegBitReader this.MarkerPosition = 0; this.badData = false; this.NoData = false; + this.eofHitCount = 0; } /// @@ -80,6 +84,9 @@ internal struct JpegBitReader [MethodImpl(InliningOptions.ShortMethod)] public bool HasBadMarker() => this.Marker != JpegConstants.Markers.XFF && !this.HasRestartMarker(); + [MethodImpl(InliningOptions.ShortMethod)] + public bool HasEndMarker() => this.Marker == JpegConstants.Markers.EOI; + [MethodImpl(InliningOptions.AlwaysInline)] public void FillBuffer() { @@ -219,11 +226,16 @@ internal struct JpegBitReader // we know we have hit the EOI and completed decoding the scan buffer. if (value == -1 || (this.badData && this.data == 0 && this.stream.Position >= this.stream.Length)) { - // We've encountered the end of the file stream which means there's no EOI marker + // We've passed the end of the file stream which means there's no EOI marker // in the image or the SOS marker has the wrong dimensions set. - this.badData = true; - this.NoData = true; - value = 0; + if (this.eofHitCount > JpegConstants.Huffman.FetchLoop) + { + this.badData = true; + this.NoData = true; + value = 0; + } + + this.eofHitCount++; } return value; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index c8d93f6e9..6a94a98ac 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -364,4 +364,15 @@ public partial class JpegDecoderTests image.DebugSave(provider); image.CompareToOriginal(provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2638 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2638, PixelTypes.Rgba32)] + public void Issue2638_DecodeWorks(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(JpegDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6be8ff6a6..5da581e52 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -316,6 +316,7 @@ public static class TestImages public const string HangBadScan = "Jpg/issues/Hang_C438A851.jpg"; public const string Issue2517 = "Jpg/issues/issue2517-bad-d7.jpg"; public const string Issue2067_CommentMarker = "Jpg/issues/issue-2067-comment.jpg"; + public const string Issue2638 = "Jpg/issues/Issue2638.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/Issue2638.jpg b/tests/Images/Input/Jpg/issues/Issue2638.jpg new file mode 100644 index 000000000..f42d67b0e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue2638.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:208d5b0b727bbef120a7e090e020a48f99c9e264c2d3939ba749f8620853c1fe +size 70876 From 45bc54ac4486ada9c0fe3b9ec35f33f23c174ce5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 19 Mar 2024 00:31:57 +1000 Subject: [PATCH 084/220] Remove unused code. --- .../Formats/Jpeg/Components/Decoder/JpegBitReader.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs index c2b0cb6e0..babd2ff4d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs @@ -84,9 +84,6 @@ internal struct JpegBitReader [MethodImpl(InliningOptions.ShortMethod)] public bool HasBadMarker() => this.Marker != JpegConstants.Markers.XFF && !this.HasRestartMarker(); - [MethodImpl(InliningOptions.ShortMethod)] - public bool HasEndMarker() => this.Marker == JpegConstants.Markers.EOI; - [MethodImpl(InliningOptions.AlwaysInline)] public void FillBuffer() { From 8acecdaf3d1d7796c606667f856f79111f2506bb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 19 Mar 2024 00:35:23 +1000 Subject: [PATCH 085/220] Update comment --- src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs index babd2ff4d..e71d86a1d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs @@ -223,7 +223,7 @@ internal struct JpegBitReader // we know we have hit the EOI and completed decoding the scan buffer. if (value == -1 || (this.badData && this.data == 0 && this.stream.Position >= this.stream.Length)) { - // We've passed the end of the file stream which means there's no EOI marker + // We've hit the end of the file stream more times than allowed which means there's no EOI marker // in the image or the SOS marker has the wrong dimensions set. if (this.eofHitCount > JpegConstants.Huffman.FetchLoop) { From 3703cb17af42798f9135393f5c7000bf7b825123 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 00:00:08 +1000 Subject: [PATCH 086/220] Wire up Rgb working spaces --- .../ChromaticAdaptionWhitePointSource.cs | 20 ++ src/ImageSharp/ColorProfiles/CieLab.cs | 3 + src/ImageSharp/ColorProfiles/CieLch.cs | 3 + .../CieXyChromaticityCoordinates.cs | 83 ++++++ src/ImageSharp/ColorProfiles/CieXyz.cs | 3 + .../ColorProfiles/ColorConversionOptions.cs | 12 + ...rProfileConverterExtensionsCieLabCieXyz.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 4 +- .../Companding/CompandingUtilities.cs | 171 ++++++++++++ .../Companding/GammaCompanding.cs | 56 ++++ .../ColorProfiles/Companding/LCompanding.cs | 71 +++++ .../Companding/Rec2020Companding.cs | 75 ++++++ .../Companding/Rec709Companding.cs | 71 +++++ .../Companding/SRgbCompanding.cs | 71 +++++ ...elf,TProfileSpace}.cs => IColorProfile.cs} | 46 ++-- src/ImageSharp/ColorProfiles/Lms.cs | 3 + src/ImageSharp/ColorProfiles/Rgb.cs | 246 ++++++++++++++++++ .../RgbPrimariesChromaticityCoordinates.cs | 82 ++++++ .../ColorProfiles/RgbWorkingSpaces.cs | 114 ++++++++ .../VonKriesChromaticAdaptation.cs | 44 +++- .../WorkingSpaces/GammaWorkingSpace.cs | 68 +++++ .../WorkingSpaces/LWorkingSpace.cs | 35 +++ .../WorkingSpaces/Rec2020WorkingSpace.cs | 35 +++ .../WorkingSpaces/Rec709WorkingSpace.cs | 35 +++ .../WorkingSpaces/RgbWorkingSpace.cs | 85 ++++++ .../WorkingSpaces/SRgbWorkingSpace.cs | 35 +++ 27 files changed, 1445 insertions(+), 36 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs create mode 100644 src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/LCompanding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs rename src/ImageSharp/ColorProfiles/{IColorProfile{TSelf,TProfileSpace}.cs => IColorProfile.cs} (82%) create mode 100644 src/ImageSharp/ColorProfiles/Rgb.cs create mode 100644 src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs create mode 100644 src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs diff --git a/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs new file mode 100644 index 000000000..7e4a9c413 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Enumerate the possible sources of the white point used in chromatic adaptation. +/// +public enum ChromaticAdaptionWhitePointSource +{ + /// + /// The white point of the source color space. + /// + WhitePoint, + + /// + /// The white point of the source working space. + /// + RgbWorkingSpace +} diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 248559f73..3578e2713 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -172,4 +172,7 @@ public readonly struct CieLab : IProfileConnectingSpace destination[i] = lab.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 4b48e1c8d..23f250d0c 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -163,4 +163,7 @@ public readonly struct CieLch : IColorProfile destination[i] = lch.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs new file mode 100644 index 000000000..b55abdd05 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +// ReSharper disable CompareOfFloatsByEqualityOperator +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the coordinates of CIEXY chromaticity space. +/// +public readonly struct CieXyChromaticityCoordinates : IEquatable +{ + /// + /// Initializes a new instance of the struct. + /// + /// Chromaticity coordinate x (usually from 0 to 1) + /// Chromaticity coordinate y (usually from 0 to 1) + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyChromaticityCoordinates(float x, float y) + { + this.X = x; + this.Y = y; + } + + /// + /// Gets the chromaticity X-coordinate. + /// + /// + /// Ranges usually from 0 to 1. + /// + public readonly float X { get; } + + /// + /// Gets the chromaticity Y-coordinate + /// + /// + /// Ranges usually from 0 to 1. + /// + public readonly float Y { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) + => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) + => !left.Equals(right); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() + => HashCode.Combine(this.X, this.Y); + + /// + public override string ToString() + => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is CieXyChromaticityCoordinates other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(CieXyChromaticityCoordinates other) + => this.X.Equals(other.X) && this.Y.Equals(other.Y); +} diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index b22ab16f9..ec28a232d 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -121,4 +121,7 @@ public readonly struct CieXyz : IProfileConnectingSpace Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); source.CopyTo(destination[..source.Length]); } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index de32aa54d..384ab6a7f 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.ColorProfiles; @@ -33,6 +35,16 @@ public class ColorConversionOptions /// public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + /// + /// Gets the source working space used for companding in conversions from/to XYZ color space. + /// + public RgbWorkingSpace RgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + + /// + /// Gets the destination working space used for companding in conversions from/to XYZ color space. + /// + public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + /// /// Gets the transformation matrix used in conversion to perform chromatic adaptation. /// diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 481280b85..8860777cd 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsTo); + VonKriesChromaticAdaptation.Transform(options, in pcsTo); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +44,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index 949cd6c8e..50317311c 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index ebe07003e..2380bb15e 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs new file mode 100644 index 000000000..44dae35a9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs @@ -0,0 +1,171 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Collections.Concurrent; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Companding utilities that allow the accelerated compression-expansion of color channels. +/// +public static class CompandingUtilities +{ + private const int Length = Scale + 2; // 256kb @ 16bit precision. + private const int Scale = (1 << 16) - 1; + private static readonly ConcurrentDictionary<(Type, double), Lazy> LookupTables = new(); + + /// + /// Lazily creates and stores a companding lookup table using the given function and modifier. + /// + /// The type of companding function. + /// The companding function. + /// A modifier to pass to the function. + /// The array. + public static Lazy GetLookupTable(Func compandingFunction, double modifier = 0) + => LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true)); + + /// + /// Creates a companding lookup table using the given function. + /// + /// The companding function. + /// A modifier to pass to the function. + /// The array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float[] CreateLookupTableImpl(Func compandingFunction, double modifier = 0) + { + float[] result = new float[Length]; + + for (int i = 0; i < result.Length; i++) + { + double d = (double)i / Scale; + d = compandingFunction(d, modifier); + result[i] = (float)d; + } + + return result; + } + + /// + /// Performs the companding operation on the given vectors using the given table. + /// + /// The span of vectors. + /// The lookup table. + public static void Compand(Span vectors, float[] table) + { + DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table)); + + if (Avx2.IsSupported && vectors.Length >= 2) + { + CompandAvx2(vectors, table); + + if (Numerics.Modulo2(vectors.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + ref Vector4 last = ref MemoryMarshal.GetReference(vectors[^1..]); + last = Compand(last, table); + } + } + else + { + CompandScalar(vectors, table); + } + } + + /// + /// Performs the companding operation on the given vector using the given table. + /// + /// The vector. + /// The lookup table. + /// The + public static Vector4 Compand(Vector4 vector, float[] table) + { + DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table)); + + Vector4 zero = Vector4.Zero; + Vector4 scale = new(Scale); + + Vector4 multiplied = Numerics.Clamp(vector * Scale, zero, scale); + + float f0 = multiplied.X; + float f1 = multiplied.Y; + float f2 = multiplied.Z; + + uint i0 = (uint)f0; + uint i1 = (uint)f1; + uint i2 = (uint)f2; + + // Alpha is already a linear representation of opacity so we do not want to convert it. + vector.X = Numerics.Lerp(table[i0], table[i0 + 1], f0 - (int)i0); + vector.Y = Numerics.Lerp(table[i1], table[i1 + 1], f1 - (int)i1); + vector.Z = Numerics.Lerp(table[i2], table[i2 + 1], f2 - (int)i2); + + return vector; + } + + private static unsafe void CompandAvx2(Span vectors, float[] table) + { + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) + { + Vector256 scale = Vector256.Create((float)Scale); + Vector256 zero = Vector256.Zero; + Vector256 offset = Vector256.Create(1); + + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); + ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); + + while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) + { + Vector256 multiplied = Avx.Multiply(scale, vectorsBase); + multiplied = Avx.Min(Avx.Max(zero, multiplied), scale); + + Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied); + Vector256 truncatedF = Avx.ConvertToVector256Single(truncated); + + Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float)); + Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float)); + + // Alpha is already a linear representation of opacity so we do not want to convert it. + Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF)); + vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl); + vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); + } + } + } + + private static unsafe void CompandScalar(Span vectors, float[] table) + { + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) + { + Vector4 zero = Vector4.Zero; + Vector4 scale = new(Scale); + ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors); + ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length); + + while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) + { + Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale); + + float f0 = multiplied.X; + float f1 = multiplied.Y; + float f2 = multiplied.Z; + + uint i0 = (uint)f0; + uint i1 = (uint)f1; + uint i2 = (uint)f2; + + // Alpha is already a linear representation of opacity so we do not want to convert it. + vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0); + vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1); + vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2); + + vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); + } + } + } +} diff --git a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs new file mode 100644 index 000000000..28e9a4641 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements gamma companding. +/// +/// +/// +/// +/// +public static class GammaCompanding +{ + private static Func CompressFunction => (d, m) => Math.Pow(d, 1 / m); + + private static Func ExpandFunction => Math.Pow; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + /// The gamma value. + public static void Compress(Span vectors, double gamma) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + /// The gamma value. + public static void Expand(Span vectors, double gamma) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The gamma value. + /// The . + public static Vector4 Compress(Vector4 vector, double gamma) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The gamma value. + /// The . + public static Vector4 Expand(Vector4 vector, double gamma) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + + private class GammaCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs new file mode 100644 index 000000000..79349d1c5 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements L* companding. +/// +/// +/// For more info see: +/// +/// +/// +public static class LCompanding +{ + private static Func CompressFunction + => (d, _) => + { + if (d <= CieConstants.Epsilon) + { + return (d * CieConstants.Kappa) / 100; + } + + return (1.16 * Math.Pow(d, 0.3333333)) - 0.16; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d <= 0.08) + { + return (100 * d) / CieConstants.Kappa; + } + + return Numerics.Pow3(((float)(d + 0.16f)) / 1.16f); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class LCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs new file mode 100644 index 000000000..9beab3a86 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements Rec. 2020 companding function. +/// +/// +/// +/// +public static class Rec2020Companding +{ + private const double Alpha = 1.09929682680944; + private const double AlphaMinusOne = Alpha - 1; + private const double Beta = 0.018053968510807; + private const double InverseBeta = Beta * 4.5; + private const double Epsilon = 1 / 0.45; + + private static Func CompressFunction + => (d, _) => + { + if (d < Beta) + { + return 4.5 * d; + } + + return (Alpha * Math.Pow(d, 0.45)) - AlphaMinusOne; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d < InverseBeta) + { + return d / 4.5; + } + + return Math.Pow((d + AlphaMinusOne) / Alpha, Epsilon); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class Rec2020CompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs new file mode 100644 index 000000000..93a32672c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements the Rec. 709 companding function. +/// +/// +/// http://en.wikipedia.org/wiki/Rec._709 +/// +public static class Rec709Companding +{ + private const double Epsilon = 1 / 0.45; + + private static Func CompressFunction + => (d, _) => + { + if (d < 0.018) + { + return 4.5 * d; + } + + return (1.099 * Math.Pow(d, 0.45)) - 0.099; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d < 0.081) + { + return d / 4.5; + } + + return Math.Pow((d + 0.099) / 1.099, Epsilon); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class Rec2020CompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs new file mode 100644 index 000000000..04e4007a8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements sRGB companding. +/// +/// +/// For more info see: +/// +/// +/// +public static class SRgbCompanding +{ + private static Func CompressFunction + => (d, _) => + { + if (d <= (0.04045 / 12.92)) + { + return d * 12.92; + } + + return (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d <= 0.04045) + { + return d / 12.92; + } + + return Math.Pow((d + 0.055) / 1.055, 2.4); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class SRgbCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs b/src/ImageSharp/ColorProfiles/IColorProfile.cs similarity index 82% rename from src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs rename to src/ImageSharp/ColorProfiles/IColorProfile.cs index e1d4cae35..ac4b425fe 100644 --- a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs +++ b/src/ImageSharp/ColorProfiles/IColorProfile.cs @@ -1,24 +1,29 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; +/// +/// Defines the contract for all color profiles. +/// +public interface IColorProfile +{ + /// + /// Gets the chromatic adaption white point source. + /// + /// The . + public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource(); +} + /// /// Defines the contract for all color profiles. /// /// The type of color profile. /// The type of color profile connecting space. -public interface IColorProfile : IEquatable +public interface IColorProfile : IColorProfile, IEquatable where TSelf : IColorProfile where TProfileSpace : struct, IProfileConnectingSpace { - /// - /// Converts the color to the profile connection space. - /// - /// The color profile conversion options. - /// The . - public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); - #pragma warning disable CA1000 // Do not declare static members on generic types /// /// Converts the color from the profile connection space. @@ -28,14 +33,6 @@ public interface IColorProfile : IEquatable /// The . public static abstract TSelf FromProfileConnectingSpace(ColorConversionOptions options, in TProfileSpace source); - /// - /// Converts the span of colors to the profile connection space. - /// - /// The color profile conversion options. - /// The color span to convert from. - /// The color profile span to write the results to. - public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); - /// /// Converts the span of colors from the profile connection space. /// @@ -43,5 +40,20 @@ public interface IColorProfile : IEquatable /// The color profile span to convert from. /// The color span to write the results to. public static abstract void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); + + /// + /// Converts the color to the profile connection space. + /// + /// The color profile conversion options. + /// The . + public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); + + /// + /// Converts the span of colors to the profile connection space. + /// + /// The color profile conversion options. + /// The color span to convert from. + /// The color profile span to write the results to. + public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); #pragma warning restore CA1000 // Do not declare static members on generic types } diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index e11c1ca87..cb060185d 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -133,4 +133,7 @@ internal readonly struct Lms : IColorProfile destination[i] = lms.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs new file mode 100644 index 000000000..2669d2c96 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -0,0 +1,246 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal readonly struct Rgb : IProfileConnectingSpace +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = Vector3.One; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component ranging between 0 and 1. + /// The green component ranging between 0 and 1. + /// The blue component ranging between 0 and 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb(float r, float g, float b) + : this(new Vector3(r, g, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the r, g, b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Rgb(Vector3 source) + { + source = Vector3.Clamp(source, Min, Max); + this.R = source.X; + this.G = source.Y; + this.B = source.Z; + } + + /// + /// Gets the red component. + /// A value usually ranging between 0 and 1. + /// + public readonly float R { get; } + + /// + /// Gets the green component. + /// A value usually ranging between 0 and 1. + /// + public readonly float G { get; } + + /// + /// Gets the blue component. + /// A value usually ranging between 0 and 1. + /// + public readonly float B { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb left, Rgb right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); + + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rgb other) + => this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B); + + /// + public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Convert to linear rgb then compress. + Rgb linear = new(Vector3.Transform(source.ToVector3(), GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace))); + return FromScaledVector4(options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4())); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Matrix4x4 matrix = GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace); + for (int i = 0; i < source.Length; i++) + { + // Convert to linear rgb then compress. + Rgb linear = new(Vector3.Transform(source[i].ToVector3(), matrix)); + Vector4 nonlinear = options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4()); + destination[i] = FromScaledVector4(nonlinear); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // First expand to linear rgb + Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(this.ToScaledVector4())); + + // Then convert to xyz + return new CieXyz(Vector3.Transform(linear.ToScaledVector3(), GetRgbToCieXyzMatrix(options.RgbWorkingSpace))); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.RgbWorkingSpace); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + + // First expand to linear rgb + Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(rgb.ToScaledVector4())); + + // Then convert to xyz + destination[i] = new CieXyz(Vector3.Transform(linear.ToScaledVector3(), matrix)); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + /// Initializes the pixel instance from a generic scaled . + /// + /// The vector to load the pixel from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb FromScaledVector3(Vector3 source) => new(source); + + /// + /// Initializes the pixel instance from a generic scaled . + /// + /// The vector to load the pixel from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z); + + /// + /// Expands the color into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToScaledVector3() => new(this.R, this.G, this.B); + + /// + /// Expands the color into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => new(this.R, this.G, this.B, 1f); + + private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) + { + Matrix4x4 matrix = GetRgbToCieXyzMatrix(workingSpace); + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + return inverseMatrix; + } + + private static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) + { + DebugGuard.NotNull(workingSpace, nameof(workingSpace)); + RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; + + float xr = chromaticity.R.X; + float xg = chromaticity.G.X; + float xb = chromaticity.B.X; + float yr = chromaticity.R.Y; + float yg = chromaticity.G.Y; + float yb = chromaticity.B.Y; + + float mXr = xr / yr; + float mZr = (1 - xr - yr) / yr; + + float mXg = xg / yg; + float mZg = (1 - xg - yg) / yg; + + float mXb = xb / yb; + float mZb = (1 - xb - yb) / yb; + + Matrix4x4 xyzMatrix = new() + { + M11 = mXr, + M21 = mXg, + M31 = mXb, + M12 = 1F, + M22 = 1F, + M32 = 1F, + M13 = mZr, + M23 = mZg, + M33 = mZb, + M44 = 1F + }; + + Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); + + Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); + + // Use transposed Rows/Columns + // TODO: Is there a built in method for this multiplication? + return new Matrix4x4 + { + M11 = vector.X * mXr, + M21 = vector.Y * mXg, + M31 = vector.Z * mXb, + M12 = vector.X * 1, + M22 = vector.Y * 1, + M32 = vector.Z * 1, + M13 = vector.X * mZr, + M23 = vector.Y * mZg, + M33 = vector.Z * mZb, + M44 = 1F + }; + } +} diff --git a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs new file mode 100644 index 000000000..90a5a57f9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the chromaticity coordinates of RGB primaries. +/// One of the specifiers of . +/// +public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable +{ + /// + /// Initializes a new instance of the struct. + /// + /// The chromaticity coordinates of the red channel. + /// The chromaticity coordinates of the green channel. + /// The chromaticity coordinates of the blue channel. + public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Gets the chromaticity coordinates of the red channel. + /// + public CieXyChromaticityCoordinates R { get; } + + /// + /// Gets the chromaticity coordinates of the green channel. + /// + public CieXyChromaticityCoordinates G { get; } + + /// + /// Gets the chromaticity coordinates of the blue channel. + /// + public CieXyChromaticityCoordinates B { get; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) + => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) + => !left.Equals(right); + + /// + public override bool Equals(object? obj) + => obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other); + + /// + public bool Equals(RgbPrimariesChromaticityCoordinates other) + => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); +} diff --git a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs new file mode 100644 index 000000000..c0d7f5074 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.Companding; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ColorProfiles; + +/// +/// Chromaticity coordinates based on: +/// +public static class RgbWorkingSpaces +{ + /// + /// sRgb working space. + /// + /// + /// Uses proper companding function, according to: + /// + /// + public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Simplified sRgb working space (uses gamma companding instead of ). + /// See also . + /// + public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Rec. 709 (ITU-R Recommendation BT.709) working space. + /// + public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + + /// + /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. + /// + public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + + /// + /// ECI Rgb v2 working space. + /// + public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + + /// + /// Adobe Rgb (1998) working space. + /// + public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Apple sRgb working space. + /// + public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + + /// + /// Best Rgb working space. + /// + public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + + /// + /// Beta Rgb working space. + /// + public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + + /// + /// Bruce Rgb working space. + /// + public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// CIE Rgb working space. + /// + public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + + /// + /// ColorMatch Rgb working space. + /// + public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + + /// + /// Don Rgb 4 working space. + /// + public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + + /// + /// Ekta Space PS5 working space. + /// + public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + + /// + /// NTSC Rgb working space. + /// + public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + + /// + /// PAL/SECAM Rgb working space. + /// + public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// ProPhoto Rgb working space. + /// + public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + + /// + /// SMPTE-C Rgb working space. + /// + public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + + /// + /// Wide Gamut Rgb working space. + /// + public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); +} diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs index 0505395de..4719750b7 100644 --- a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -19,16 +19,25 @@ public static class VonKriesChromaticAdaptation /// /// Performs a linear transformation of a source color in to the destination color. /// + /// The type of color profile to convert from. + /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). /// The color profile conversion options. /// The source color. /// The - public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile { - CieXyz sourceWhitePoint = options.WhitePoint; - CieXyz destinationWhitePoint = options.TargetWhitePoint; + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.WhitePoint + : options.RgbWorkingSpace.WhitePoint; - if (sourceWhitePoint.Equals(destinationWhitePoint)) + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.TargetWhitePoint + : options.TargetRgbWorkingSpace.WhitePoint; + + if (sourceWhitePoint.Equals(targetWhitePoint)) { return new(source.X, source.Y, source.Z); } @@ -37,7 +46,7 @@ public static class VonKriesChromaticAdaptation Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); @@ -49,19 +58,28 @@ public static class VonKriesChromaticAdaptation /// /// Performs a bulk linear transformation of a source color in to the destination color. /// + /// The type of color profile to convert from. + /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). /// The color profile conversion options. /// The span to the source colors. /// The span to the destination colors. - public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); int count = source.Length; - CieXyz sourceWhitePoint = options.WhitePoint; - CieXyz destinationWhitePoint = options.TargetWhitePoint; + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.WhitePoint + : options.RgbWorkingSpace.WhitePoint; + + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.TargetWhitePoint + : options.TargetRgbWorkingSpace.WhitePoint; - if (sourceWhitePoint.Equals(destinationWhitePoint)) + if (sourceWhitePoint.Equals(targetWhitePoint)) { source.CopyTo(destination[..count]); return; @@ -73,16 +91,18 @@ public static class VonKriesChromaticAdaptation ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceBase, i); ref CieXyz dp = ref Unsafe.Add(ref destinationBase, i); Vector3 sourceColorLms = Vector3.Transform(sp.ToVector3(), matrix); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); - Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); dp = new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); } diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs new file mode 100644 index 000000000..91fa42624 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// The gamma working space. +/// +public sealed class GammaWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The gamma value. + /// The reference white point. + /// The chromaticity of the rgb primaries. + public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; + + /// + /// Gets the gamma value. + /// + public float Gamma { get; } + + /// + public override void Compress(Span vectors) => GammaCompanding.Compress(vectors, this.Gamma); + + /// + public override void Expand(Span vectors) => GammaCompanding.Expand(vectors, this.Gamma); + + /// + public override Vector4 Compress(Vector4 vector) => GammaCompanding.Compress(vector, this.Gamma); + + /// + public override Vector4 Expand(Vector4 vector) => GammaCompanding.Expand(vector, this.Gamma); + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is GammaWorkingSpace other) + { + return this.Gamma.Equals(other.Gamma) + && this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() => HashCode.Combine( + this.WhitePoint, + this.ChromaticityCoordinates, + this.Gamma); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs new file mode 100644 index 000000000..77dc2c06d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// L* working space. +/// +public sealed class LWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => LCompanding.Compress(vectors); + + /// + public override void Expand(Span vectors) => LCompanding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => LCompanding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => LCompanding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs new file mode 100644 index 000000000..970d10302 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. +/// +public sealed class Rec2020WorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => Rec2020Companding.Compress(vectors); + + /// + public override void Expand(Span vectors) => Rec2020Companding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => Rec2020Companding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => Rec2020Companding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs new file mode 100644 index 000000000..011da5807 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Rec. 709 (ITU-R Recommendation BT.709) working space. +/// +public sealed class Rec709WorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => Rec709Companding.Compress(vectors); + + /// + public override void Expand(Span vectors) => Rec709Companding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => Rec709Companding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => Rec709Companding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs new file mode 100644 index 000000000..97069b856 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs @@ -0,0 +1,85 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Base class for all implementations of . +/// +public abstract class RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + /// Gets the reference white point + /// + public CieXyz WhitePoint { get; } + + /// + /// Gets the chromaticity of the rgb primaries. + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public abstract void Compress(Span vectors); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public abstract void Expand(Span vectors); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public abstract Vector4 Compress(Vector4 vector); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public abstract Vector4 Expand(Vector4 vector); + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is RgbWorkingSpace other) + { + return this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() + => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs new file mode 100644 index 000000000..b88e6c898 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// The sRgb working space. +/// +public sealed class SRgbWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => SRgbCompanding.Compress(vectors); + + /// + public override void Expand(Span vectors) => SRgbCompanding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => SRgbCompanding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => SRgbCompanding.Expand(vector); +} From 66bbc6af24192da328b359ca095cdc2ecb8ba239 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 12:32:28 +1000 Subject: [PATCH 087/220] Remove clamping --- src/ImageSharp/ColorProfiles/Rgb.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 2669d2c96..bccae1132 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -9,19 +9,18 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal readonly struct Rgb : IProfileConnectingSpace { - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - /// /// Initializes a new instance of the struct. /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. + /// The red component usually ranging between 0 and 1. + /// The green component usually ranging between 0 and 1. + /// The blue component usually ranging between 0 and 1. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b) - : this(new Vector3(r, g, b)) { + this.R = r; + this.G = g; + this.B = b; } /// @@ -31,7 +30,6 @@ internal readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] private Rgb(Vector3 source) { - source = Vector3.Clamp(source, Min, Max); this.R = source.X; this.G = source.Y; this.B = source.Z; @@ -165,7 +163,7 @@ internal readonly struct Rgb : IProfileConnectingSpace /// /// Expands the color into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. + /// with values scaled and usually clamped between 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . @@ -174,7 +172,7 @@ internal readonly struct Rgb : IProfileConnectingSpace /// /// Expands the color into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. + /// with values scaled and usually clamped between 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . From cb71ae24360fb0a41b1d3e8529cfc6dd9239b6d0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 17:44:50 +1000 Subject: [PATCH 088/220] Complete Rgb conversion --- ...rProfileConverterExtensionsCieLabCieXyz.cs | 2 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 2 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 14 +- .../Companding/CompandingUtilities.cs | 19 ++- .../Companding/GammaCompanding.cs | 10 +- .../ColorProfiles/Companding/LCompanding.cs | 10 +- .../Companding/Rec2020Companding.cs | 10 +- .../Companding/Rec709Companding.cs | 10 +- .../Companding/SRgbCompanding.cs | 10 +- .../ApproximateColorProfileComparer.cs | 7 +- .../RgbAndCieXyzConversionTest.cs | 151 ++++++++++++++++++ 11 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 8860777cd..cf9f91801 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsTo); + pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index 50317311c..a73677129 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index 2380bb15e..fd6ff21e4 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,13 +18,10 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); - - // Convert between PCS - CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, pcsFrom); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) @@ -41,12 +38,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz // Adapt to target white point VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); - // Convert between PCS. - using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); - Span pcsTo = pcsToOwner.GetSpan(); - CieXyz.FromProfileConnectionSpace(options, pcsFrom, pcsTo); - // Convert to output from PCS - TTo.FromProfileConnectionSpace(options, pcsTo, destination); + TTo.FromProfileConnectionSpace(options, pcsFrom, destination); } } diff --git a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs index 44dae35a9..1970e2d94 100644 --- a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs +++ b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs @@ -17,17 +17,28 @@ public static class CompandingUtilities { private const int Length = Scale + 2; // 256kb @ 16bit precision. private const int Scale = (1 << 16) - 1; - private static readonly ConcurrentDictionary<(Type, double), Lazy> LookupTables = new(); + private static readonly ConcurrentDictionary<(Type, double), float[]> CompressLookupTables = new(); + private static readonly ConcurrentDictionary<(Type, double), float[]> ExpandLookupTables = new(); /// - /// Lazily creates and stores a companding lookup table using the given function and modifier. + /// Lazily creates and stores a companding compression lookup table using the given function and modifier. /// /// The type of companding function. /// The companding function. /// A modifier to pass to the function. /// The array. - public static Lazy GetLookupTable(Func compandingFunction, double modifier = 0) - => LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true)); + public static float[] GetCompressLookupTable(Func compandingFunction, double modifier = 0) + => CompressLookupTables.GetOrAdd((typeof(T), modifier), args => CreateLookupTableImpl(compandingFunction, args.Item2)); + + /// + /// Lazily creates and stores a companding expanding lookup table using the given function and modifier. + /// + /// The type of companding function. + /// The companding function. + /// A modifier to pass to the function. + /// The array. + public static float[] GetExpandLookupTable(Func compandingFunction, double modifier = 0) + => ExpandLookupTables.GetOrAdd((typeof(T), modifier), args => CreateLookupTableImpl(compandingFunction, args.Item2)); /// /// Creates a companding lookup table using the given function. diff --git a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs index 28e9a4641..34ca8bf5e 100644 --- a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs @@ -24,7 +24,7 @@ public static class GammaCompanding /// The span of vectors. /// The gamma value. public static void Compress(Span vectors, double gamma) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction, gamma)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. @@ -32,7 +32,7 @@ public static class GammaCompanding /// The span of vectors. /// The gamma value. public static void Expand(Span vectors, double gamma) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction, gamma)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -41,16 +41,16 @@ public static class GammaCompanding /// The gamma value. /// The . public static Vector4 Compress(Vector4 vector, double gamma) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction, gamma)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The gamma value. /// The . public static Vector4 Expand(Vector4 vector, double gamma) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction, gamma)); private class GammaCompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs index 79349d1c5..4f5383038 100644 --- a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs @@ -42,14 +42,14 @@ public static class LCompanding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class LCompanding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class LCompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs index 9beab3a86..1901e6471 100644 --- a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs @@ -46,14 +46,14 @@ public static class Rec2020Companding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -61,15 +61,15 @@ public static class Rec2020Companding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class Rec2020CompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs index 93a32672c..94b17d8d0 100644 --- a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs @@ -42,14 +42,14 @@ public static class Rec709Companding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class Rec709Companding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class Rec2020CompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs index 04e4007a8..ab2710230 100644 --- a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs @@ -42,14 +42,14 @@ public static class SRgbCompanding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class SRgbCompanding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class SRgbCompandingKey; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 5c2f9afbd..84319b66c 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -13,7 +13,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -31,6 +32,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieLch x, CieLch y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + public bool Equals(Rgb x, Rgb y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -39,6 +42,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieLch obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Rgb obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs new file mode 100644 index 000000000..5e867178a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -0,0 +1,151 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class RgbAndCieXyzConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0.96422, 1.00000, 0.82521, 1, 1, 1)] + [InlineData(0.00000, 1.00000, 0.00000, 0, 1, 0)] + [InlineData(0.96422, 0.00000, 0.00000, 1, 0, 0.292064)] + [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] + public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) + { + // Arrange + CieXyz input = new(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + Rgb expected = new(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.950470, 1.000000, 1.088830, 1, 1, 1)] + [InlineData(0, 1.000000, 0, 0, 1, 0)] + [InlineData(0.950470, 0, 0, 1, 0, 0.254967)] + [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] + public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) + { + // Arrange + CieXyz input = new(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + Rgb expected = new(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0.964220, 1.000000, 0.825210)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 0, 0, 0.436075, 0.222504, 0.013932)] + [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] + [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] + [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] + public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) + { + // Arrange + Rgb input = new(r, g, b); + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0.950470, 1.000000, 1.088830)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 0, 0, 0.412456, 0.212673, 0.019334)] + [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] + [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] + [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] + public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) + { + // Arrange + Rgb input = new(r, g, b); + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 80dc3f51d452894c664341f05fb3ac0aca1b1e14 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 20:44:33 +1000 Subject: [PATCH 089/220] Don't over allocate --- .../ColorProfileConverterExtensionsCieLabCieXyz.cs | 4 ++-- src/ImageSharp/ColorProfiles/IColorProfile.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index cf9f91801..545b5dadf 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -39,8 +39,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Convert between PCS. - using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length * 2); - Span pcsTo = pcsToOwner.GetSpan()[..source.Length]; + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point diff --git a/src/ImageSharp/ColorProfiles/IColorProfile.cs b/src/ImageSharp/ColorProfiles/IColorProfile.cs index ac4b425fe..6a1b2ee8d 100644 --- a/src/ImageSharp/ColorProfiles/IColorProfile.cs +++ b/src/ImageSharp/ColorProfiles/IColorProfile.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -11,7 +11,7 @@ public interface IColorProfile /// /// Gets the chromatic adaption white point source. /// - /// The . + /// The . public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource(); } From 2067026c0933ced15027db458b9d7d641ed7a65c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 15 Mar 2024 20:02:00 +1000 Subject: [PATCH 090/220] Ensure VP8X alpha flag is updated correctly. --- .../Formats/Webp/BitWriter/BitWriterBase.cs | 26 +++++++++++++----- .../Formats/Webp/Chunks/WebpVp8X.cs | 23 +++++++++++++++- .../Formats/Webp/Lossless/Vp8LEncoder.cs | 20 +++++++++----- .../Formats/Webp/Lossy/Vp8Encoder.cs | 24 +++++++++++------ src/ImageSharp/Formats/Webp/RiffHelper.cs | 17 +++++++++++- .../Formats/Webp/WebpChunkParsingUtils.cs | 1 - .../Formats/Webp/WebpDecoderCore.cs | 2 +- .../Formats/Webp/WebpEncoderCore.cs | 27 ++++++++++++------- 8 files changed, 105 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs index 927992686..39c4beb61 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs @@ -88,7 +88,8 @@ internal abstract class BitWriterBase /// The color profile. /// Flag indicating, if a alpha channel is present. /// Flag indicating, if an animation parameter is present. - public static void WriteTrunksBeforeData( + /// A or a default instance. + public static WebpVp8X WriteTrunksBeforeData( Stream stream, uint width, uint height, @@ -102,16 +103,19 @@ internal abstract class BitWriterBase RiffHelper.BeginWriteRiffFile(stream, WebpConstants.WebpFourCc); // Write VP8X, header if necessary. + WebpVp8X vp8x = default; bool isVp8X = exifProfile != null || xmpProfile != null || iccProfile != null || hasAlpha || hasAnimation; if (isVp8X) { - WriteVp8XHeader(stream, exifProfile, xmpProfile, iccProfile, width, height, hasAlpha, hasAnimation); + vp8x = WriteVp8XHeader(stream, exifProfile, xmpProfile, iccProfile, width, height, hasAlpha, hasAnimation); if (iccProfile != null) { RiffHelper.WriteChunk(stream, (uint)WebpChunkType.Iccp, iccProfile.ToByteArray()); } } + + return vp8x; } /// @@ -124,10 +128,16 @@ internal abstract class BitWriterBase /// Write the trunks after data trunk. /// /// The stream to write to. - /// The exif profile. + /// The VP8X chunk. + /// Whether to update the chunk. + /// The initial position of the stream before encoding. + /// The EXIF profile. /// The XMP profile. public static void WriteTrunksAfterData( Stream stream, + in WebpVp8X vp8x, + bool updateVp8x, + long initialPosition, ExifProfile? exifProfile, XmpProfile? xmpProfile) { @@ -141,7 +151,7 @@ internal abstract class BitWriterBase RiffHelper.WriteChunk(stream, (uint)WebpChunkType.Xmp, xmpProfile.Data); } - RiffHelper.EndWriteRiffFile(stream, 4); + RiffHelper.EndWriteRiffFile(stream, in vp8x, updateVp8x, initialPosition); } /// @@ -186,19 +196,21 @@ internal abstract class BitWriterBase /// Writes a VP8X header to the stream. /// /// The stream to write to. - /// A exif profile or null, if it does not exist. - /// A XMP profile or null, if it does not exist. + /// An EXIF profile or null, if it does not exist. + /// An XMP profile or null, if it does not exist. /// The color profile. /// The width of the image. /// The height of the image. /// Flag indicating, if a alpha channel is present. /// Flag indicating, if an animation parameter is present. - protected static void WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, IccProfile? iccProfile, uint width, uint height, bool hasAlpha, bool hasAnimation) + protected static WebpVp8X WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, IccProfile? iccProfile, uint width, uint height, bool hasAlpha, bool hasAnimation) { WebpVp8X chunk = new(hasAnimation, xmpProfile != null, exifProfile != null, hasAlpha, iccProfile != null, width, height); chunk.Validate(MaxDimension, MaxCanvasPixels); chunk.WriteTo(stream); + + return chunk; } } diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs index f781d6114..f60d0c837 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs @@ -3,7 +3,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Chunks; -internal readonly struct WebpVp8X +internal readonly struct WebpVp8X : IEquatable { public WebpVp8X(bool hasAnimation, bool hasXmp, bool hasExif, bool hasAlpha, bool hasIcc, uint width, uint height) { @@ -51,6 +51,24 @@ internal readonly struct WebpVp8X /// public uint Height { get; } + public static bool operator ==(WebpVp8X left, WebpVp8X right) => left.Equals(right); + + public static bool operator !=(WebpVp8X left, WebpVp8X right) => !(left == right); + + public override bool Equals(object? obj) => obj is WebpVp8X x && this.Equals(x); + + public bool Equals(WebpVp8X other) + => this.HasAnimation == other.HasAnimation + && this.HasXmp == other.HasXmp + && this.HasExif == other.HasExif + && this.HasAlpha == other.HasAlpha + && this.HasIcc == other.HasIcc + && this.Width == other.Width + && this.Height == other.Height; + + public override int GetHashCode() + => HashCode.Combine(this.HasAnimation, this.HasXmp, this.HasExif, this.HasAlpha, this.HasIcc, this.Width, this.Height); + public void Validate(uint maxDimension, ulong maxCanvasPixels) { if (this.Width > maxDimension || this.Height > maxDimension) @@ -65,6 +83,9 @@ internal readonly struct WebpVp8X } } + public WebpVp8X WithAlpha(bool hasAlpha) + => new(this.HasAnimation, this.HasXmp, this.HasExif, hasAlpha, this.HasIcc, this.Width, this.Height); + public void WriteTo(Stream stream) { byte flags = 0; diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index f15cb3eb5..f658e40f6 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -236,7 +236,7 @@ internal class Vp8LEncoder : IDisposable /// public Vp8LHashChain HashChain { get; } - public void EncodeHeader(Image image, Stream stream, bool hasAnimation) + public WebpVp8X EncodeHeader(Image image, Stream stream, bool hasAnimation) where TPixel : unmanaged, IPixel { // Write bytes from the bit-writer buffer to the stream. @@ -246,7 +246,8 @@ internal class Vp8LEncoder : IDisposable ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; - BitWriterBase.WriteTrunksBeforeData( + // The alpha flag is updated following encoding. + WebpVp8X vp8x = BitWriterBase.WriteTrunksBeforeData( stream, (uint)image.Width, (uint)image.Height, @@ -261,9 +262,11 @@ internal class Vp8LEncoder : IDisposable WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } + + return vp8x; } - public void EncodeFooter(Image image, Stream stream) + public void EncodeFooter(Image image, in WebpVp8X vp8x, bool hasAlpha, Stream stream, long initialPosition) where TPixel : unmanaged, IPixel { // Write bytes from the bit-writer buffer to the stream. @@ -272,7 +275,9 @@ internal class Vp8LEncoder : IDisposable ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; - BitWriterBase.WriteTrunksAfterData(stream, exifProfile, xmpProfile); + bool updateVp8x = hasAlpha && vp8x != default; + WebpVp8X updated = updateVp8x ? vp8x.WithAlpha(true) : vp8x; + BitWriterBase.WriteTrunksAfterData(stream, in updated, updateVp8x, initialPosition, exifProfile, xmpProfile); } /// @@ -284,7 +289,8 @@ internal class Vp8LEncoder : IDisposable /// The frame metadata. /// The to encode the image data to. /// Flag indicating, if an animation parameter is present. - public void Encode(ImageFrame frame, Rectangle bounds, WebpFrameMetadata frameMetadata, Stream stream, bool hasAnimation) + /// A indicating whether the frame contains an alpha channel. + public bool Encode(ImageFrame frame, Rectangle bounds, WebpFrameMetadata frameMetadata, Stream stream, bool hasAnimation) where TPixel : unmanaged, IPixel { // Convert image pixels to bgra array. @@ -323,6 +329,8 @@ internal class Vp8LEncoder : IDisposable { RiffHelper.EndWriteChunk(stream, prevPosition); } + + return hasAlpha; } /// @@ -501,7 +509,7 @@ internal class Vp8LEncoder : IDisposable /// The type of the pixels. /// The frame pixel buffer to convert. /// true, if the image is non opaque. - private bool ConvertPixelsToBgra(Buffer2DRegion pixels) + public bool ConvertPixelsToBgra(Buffer2DRegion pixels) where TPixel : unmanaged, IPixel { bool nonOpaque = false; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index 6e9e4f9cd..40d91ecf1 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -310,7 +310,7 @@ internal class Vp8Encoder : IDisposable /// private int MbHeaderLimit { get; } - public void EncodeHeader(Image image, Stream stream, bool hasAlpha, bool hasAnimation) + public WebpVp8X EncodeHeader(Image image, Stream stream, bool hasAlpha, bool hasAnimation) where TPixel : unmanaged, IPixel { // Write bytes from the bitwriter buffer to the stream. @@ -320,7 +320,7 @@ internal class Vp8Encoder : IDisposable ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; - BitWriterBase.WriteTrunksBeforeData( + WebpVp8X vp8x = BitWriterBase.WriteTrunksBeforeData( stream, (uint)image.Width, (uint)image.Height, @@ -335,9 +335,11 @@ internal class Vp8Encoder : IDisposable WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } + + return vp8x; } - public void EncodeFooter(Image image, Stream stream) + public void EncodeFooter(Image image, in WebpVp8X vp8x, bool hasAlpha, Stream stream, long initialPosition) where TPixel : unmanaged, IPixel { // Write bytes from the bitwriter buffer to the stream. @@ -346,7 +348,9 @@ internal class Vp8Encoder : IDisposable ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; - BitWriterBase.WriteTrunksAfterData(stream, exifProfile, xmpProfile); + bool updateVp8x = hasAlpha && vp8x != default; + WebpVp8X updated = updateVp8x ? vp8x.WithAlpha(true) : vp8x; + BitWriterBase.WriteTrunksAfterData(stream, in updated, updateVp8x, initialPosition, exifProfile, xmpProfile); } /// @@ -357,9 +361,10 @@ internal class Vp8Encoder : IDisposable /// The stream to encode the image data to. /// The region of interest within the frame to encode. /// The frame metadata. - public void EncodeAnimation(ImageFrame frame, Stream stream, Rectangle bounds, WebpFrameMetadata frameMetadata) - where TPixel : unmanaged, IPixel => - this.Encode(stream, frame, bounds, frameMetadata, true, null); + /// A indicating whether the frame contains an alpha channel. + public bool EncodeAnimation(ImageFrame frame, Stream stream, Rectangle bounds, WebpFrameMetadata frameMetadata) + where TPixel : unmanaged, IPixel + => this.Encode(stream, frame, bounds, frameMetadata, true, null); /// /// Encodes the static image frame to the specified stream. @@ -384,7 +389,8 @@ internal class Vp8Encoder : IDisposable /// The frame metadata. /// Flag indicating, if an animation parameter is present. /// The image to encode from. - private void Encode(Stream stream, ImageFrame frame, Rectangle bounds, WebpFrameMetadata frameMetadata, bool hasAnimation, Image image) + /// A indicating whether the frame contains an alpha channel. + private bool Encode(Stream stream, ImageFrame frame, Rectangle bounds, WebpFrameMetadata frameMetadata, bool hasAnimation, Image image) where TPixel : unmanaged, IPixel { int width = bounds.Width; @@ -514,6 +520,8 @@ internal class Vp8Encoder : IDisposable { encodedAlphaData?.Dispose(); } + + return hasAlpha; } /// diff --git a/src/ImageSharp/Formats/Webp/RiffHelper.cs b/src/ImageSharp/Formats/Webp/RiffHelper.cs index d3862ea8b..b6318c748 100644 --- a/src/ImageSharp/Formats/Webp/RiffHelper.cs +++ b/src/ImageSharp/Formats/Webp/RiffHelper.cs @@ -3,6 +3,7 @@ using System.Buffers.Binary; using System.Text; +using SixLabors.ImageSharp.Formats.Webp.Chunks; namespace SixLabors.ImageSharp.Formats.Webp; @@ -107,6 +108,7 @@ internal static class RiffHelper position++; } + // Add the size of the encoded file to the Riff header. BinaryPrimitives.WriteUInt32LittleEndian(buffer, dataSize); stream.Position = sizePosition; stream.Write(buffer); @@ -120,5 +122,18 @@ internal static class RiffHelper return sizePosition; } - public static void EndWriteRiffFile(Stream stream, long sizePosition) => EndWriteChunk(stream, sizePosition); + public static void EndWriteRiffFile(Stream stream, in WebpVp8X vp8x, bool updateVp8x, long sizePosition) + { + EndWriteChunk(stream, sizePosition + 4); + + // Write the VP8X chunk if necessary. + if (updateVp8x) + { + long position = stream.Position; + + stream.Position = sizePosition + 12; + vp8x.WriteTo(stream); + stream.Position = position; + } + } } diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index 80ffe8a99..07f09d45e 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers.Binary; -using System.Drawing; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.IO; diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 2991f355f..21f0f4946 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -54,7 +54,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// The flag to decide how to handle the background color in the Animation Chunk. /// - private BackgroundColorHandling backgroundColorHandling; + private readonly BackgroundColorHandling backgroundColorHandling; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index e37c1d179..d29759f9a 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Memory; @@ -143,12 +144,14 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals this.nearLossless, this.nearLosslessQuality); - encoder.EncodeHeader(image, stream, hasAnimation); + long initialPosition = stream.Position; + bool hasAlpha = false; + WebpVp8X vp8x = encoder.EncodeHeader(image, stream, hasAnimation); // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); - encoder.Encode(previousFrame, previousFrame.Bounds(), frameMetadata, stream, hasAnimation); + hasAlpha |= encoder.Encode(previousFrame, previousFrame.Bounds(), frameMetadata, stream, hasAnimation); if (hasAnimation) { @@ -190,14 +193,14 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals this.nearLossless, this.nearLosslessQuality); - animatedEncoder.Encode(encodingFrame, bounds, frameMetadata, stream, hasAnimation); + hasAlpha |= animatedEncoder.Encode(encodingFrame, bounds, frameMetadata, stream, hasAnimation); previousFrame = currentFrame; previousDisposal = frameMetadata.DisposalMethod; } } - encoder.EncodeFooter(image, stream); + encoder.EncodeFooter(image, in vp8x, hasAlpha, stream, initialPosition); } else { @@ -214,17 +217,20 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals this.spatialNoiseShaping, this.alphaCompression); + long initialPosition = stream.Position; + bool hasAlpha = false; + WebpVp8X vp8x = default; if (image.Frames.Count > 1) { - // TODO: What about alpha here? - encoder.EncodeHeader(image, stream, false, true); + // The alpha flag is updated following encoding. + vp8x = encoder.EncodeHeader(image, stream, false, true); // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod; - encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata); + hasAlpha |= encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata); // Encode additional frames // This frame is reused to store de-duplicated pixel buffers. @@ -263,18 +269,19 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals this.spatialNoiseShaping, this.alphaCompression); - animatedEncoder.EncodeAnimation(encodingFrame, stream, bounds, frameMetadata); + hasAlpha |= animatedEncoder.EncodeAnimation(encodingFrame, stream, bounds, frameMetadata); previousFrame = currentFrame; previousDisposal = frameMetadata.DisposalMethod; } + + encoder.EncodeFooter(image, in vp8x, hasAlpha, stream, initialPosition); } else { encoder.EncodeStatic(stream, image); + encoder.EncodeFooter(image, in vp8x, hasAlpha, stream, initialPosition); } - - encoder.EncodeFooter(image, stream); } } } From 6733e8d8231f17ceb906f7d971bb7abaa7c01daf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 17 Mar 2024 11:05:56 +1000 Subject: [PATCH 091/220] Update WebpVp8X.cs --- src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs index f60d0c837..491f71650 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs @@ -66,7 +66,7 @@ internal readonly struct WebpVp8X : IEquatable && this.Width == other.Width && this.Height == other.Height; - public override int GetHashCode() + public override int GetHashCode() => HashCode.Combine(this.HasAnimation, this.HasXmp, this.HasExif, this.HasAlpha, this.HasIcc, this.Width, this.Height); public void Validate(uint maxDimension, ulong maxCanvasPixels) From 5d9e3051cb50c46fca7a3832188c44285e9d8d24 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 12:33:23 -1000 Subject: [PATCH 092/220] Add test --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 3 ++- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png | 3 +++ .../Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png | 3 +++ .../Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png | 3 +++ .../Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png | 3 +++ .../Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png | 3 +++ tests/Images/Input/Png/animated/frame-offset.png | 3 +++ 8 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png create mode 100644 tests/Images/Input/Png/animated/frame-offset.png diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index de99432bc..b6e798c30 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -87,7 +87,8 @@ public partial class PngDecoderTests TestImages.Png.DisposeBackgroundRegion, TestImages.Png.DisposePreviousFirst, TestImages.Png.DisposeBackgroundBeforeRegion, - TestImages.Png.BlendOverMultiple + TestImages.Png.BlendOverMultiple, + TestImages.Png.FrameOffset }; [Theory] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 5da581e52..1a1a3cd9e 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -73,6 +73,7 @@ public static class TestImages public const string DisposeBackgroundRegion = "Png/animated/15-dispose-background-region.png"; public const string DisposePreviousFirst = "Png/animated/12-dispose-prev-first.png"; public const string BlendOverMultiple = "Png/animated/21-blend-over-multiple.png"; + public const string FrameOffset = "Png/animated/frame-offset.png"; public const string Issue2666 = "Png/issues/Issue_2666.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png new file mode 100644 index 000000000..b9fa24c93 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84e2353264e3488122f4d488d7c4b198ff5192ad0c662c7fb0a369c957ecc7ea +size 353 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png new file mode 100644 index 000000000..6f3a27187 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2916711d3f4d72eb66a5cfc2b40a3318eb4cce5b367658cfc7e3b573fd39cc33 +size 693 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png new file mode 100644 index 000000000..50911cce5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f9d5503414ccefa6b66661b1e93c2c3f6e4491f14af006a71153cecf43b52f5 +size 806 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png new file mode 100644 index 000000000..89d2f9570 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe42b7dc6524d5589ad680650f4bcd181319b40b258b31e0932d6e936818e980 +size 570 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png new file mode 100644 index 000000000..c3f2b99b8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8002ff5b3451b348f285eec15dd7a093c62d11d8b77c3ead9ac89ca6eb29977d +size 669 diff --git a/tests/Images/Input/Png/animated/frame-offset.png b/tests/Images/Input/Png/animated/frame-offset.png new file mode 100644 index 000000000..4eebb44a3 --- /dev/null +++ b/tests/Images/Input/Png/animated/frame-offset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c019073841b48b02cb07c779fed8654c6052aee700e7620d07f5d775d97088f +size 2156 From a58ef4bdb70e9421823b3bb4c6d86b28195073c0 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 13:50:40 -1000 Subject: [PATCH 093/220] Fix ProcessInterlacedPaletteScanline not obeying frameControl.XOffset --- src/ImageSharp/Formats/Png/PngScanlineProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index aa937a8e2..0f530b478 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -180,8 +180,9 @@ internal static class PngScanlineProcessor ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref Color paletteBase = ref MemoryMarshal.GetReference(palette.Value.Span); + uint offset = pixelOffset + frameControl.XOffset; - for (nuint x = pixelOffset, o = 0; x < frameControl.XMax; x += increment, o++) + for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++) { uint index = Unsafe.Add(ref scanlineSpanRef, o); Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToPixel()); From ce069bce2501bccf171bd585e8c854a58ac53687 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 17:49:33 -1000 Subject: [PATCH 094/220] Fix frame dispose operation handling --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 25 +++++++++++-------- .../00.png | 4 +-- .../01.png | 4 +-- .../02.png | 4 +-- .../03.png | 4 +-- .../04.png | 4 +-- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 6a321a3ba..23e3033dc 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -246,8 +246,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals currentFrameControl.Value, cancellationToken); - previousFrame = currentFrame; - previousFrameControl = currentFrameControl; + // if current frame dispose is restore to previous, then from future frame's perspective, it never happened + if (currentFrameControl.Value.DisposeOperation != PngDisposalMethod.RestoreToPrevious) + { + previousFrame = currentFrame; + previousFrameControl = currentFrameControl; + } + break; case PngChunkType.Data: @@ -645,18 +650,18 @@ internal sealed class PngDecoderCore : IImageDecoderInternals out ImageFrame frame) where TPixel : unmanaged, IPixel { - // We create a clone of the previous frame and add it. - // We will overpaint the difference of pixels on the current frame to create a complete image. - // This ensures that we have enough pixel data to process without distortion. #2450 frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame); - // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND. - if (previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToBackground - || (previousFrame is null && previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToPrevious)) + // if restoring to before first frame, restore to background + if (previousFrame is null && previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToPrevious) + { + Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(); + pixelRegion.Clear(); + } + else if (previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToBackground) { Rectangle restoreArea = previousFrameControl.Bounds; - Rectangle interest = Rectangle.Intersect(frame.Bounds(), restoreArea); - Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(interest); + Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(restoreArea); pixelRegion.Clear(); } diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png index b9fa24c93..870ed61a4 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84e2353264e3488122f4d488d7c4b198ff5192ad0c662c7fb0a369c957ecc7ea -size 353 +oid sha256:b85aaf7153e0ca538856a58d7b069bcc13fadc468ea603c85f8782cc691f86c3 +size 387 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png index 6f3a27187..cab85d946 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/01.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2916711d3f4d72eb66a5cfc2b40a3318eb4cce5b367658cfc7e3b573fd39cc33 -size 693 +oid sha256:fcb83d6893dcfd869b764ff9846c259eaa0caf26cec3f0fc2cbae2c26f2eeaa5 +size 660 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png index 50911cce5..1a2c5adcf 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/02.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f9d5503414ccefa6b66661b1e93c2c3f6e4491f14af006a71153cecf43b52f5 -size 806 +oid sha256:562ec382f6d2af68e66092bf6949f66147d5f608d3c618eea5a7c1ea400737ff +size 768 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png index 89d2f9570..d850459ee 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/03.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe42b7dc6524d5589ad680650f4bcd181319b40b258b31e0932d6e936818e980 -size 570 +oid sha256:d12a7791b960072e32b78bd9aaf456dc99341eea1c66ea05050433d8c082c6ac +size 579 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png index c3f2b99b8..000b0567d 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_frame-offset.png/04.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8002ff5b3451b348f285eec15dd7a093c62d11d8b77c3ead9ac89ca6eb29977d -size 669 +oid sha256:2db38d7ffcc95c23a5c94a06f10c6cc67406ae581a955c99ede4af97b1a044f8 +size 628 From b29962abca4758eabed06b526520d82ad31d514c Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 18:14:23 -1000 Subject: [PATCH 095/220] Add test for default image not animated --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 3 ++- tests/ImageSharp.Tests/TestImages.cs | 1 + .../00.png | 3 +++ .../01.png | 3 +++ tests/Images/Input/Png/animated/default-not-animated.png | 3 +++ 5 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/00.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/01.png create mode 100644 tests/Images/Input/Png/animated/default-not-animated.png diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index b6e798c30..152598ac8 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -88,7 +88,8 @@ public partial class PngDecoderTests TestImages.Png.DisposePreviousFirst, TestImages.Png.DisposeBackgroundBeforeRegion, TestImages.Png.BlendOverMultiple, - TestImages.Png.FrameOffset + TestImages.Png.FrameOffset, + TestImages.Png.DefaultNotAnimated }; [Theory] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1a1a3cd9e..5c80422da 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -74,6 +74,7 @@ public static class TestImages public const string DisposePreviousFirst = "Png/animated/12-dispose-prev-first.png"; public const string BlendOverMultiple = "Png/animated/21-blend-over-multiple.png"; public const string FrameOffset = "Png/animated/frame-offset.png"; + public const string DefaultNotAnimated = "Png/animated/default-not-animated.png"; public const string Issue2666 = "Png/issues/Issue_2666.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/00.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/00.png new file mode 100644 index 000000000..4c5ea8169 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d4716e18655be53630d6d50daebe8c38e0eedb2432c7a73840b55d1473d5944 +size 1050 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/01.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/01.png new file mode 100644 index 000000000..790fe45e4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_VerifyAllFrames_Rgba32_default-not-animated.png/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b5a6d3cf1a777f6b719c2a1cf79bffe2251355d75e6c0f7ce7a973b3d033419 +size 1177 diff --git a/tests/Images/Input/Png/animated/default-not-animated.png b/tests/Images/Input/Png/animated/default-not-animated.png new file mode 100644 index 000000000..1ed72698d --- /dev/null +++ b/tests/Images/Input/Png/animated/default-not-animated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:647d484c8f320b55824b9219270524df3edc434a4793e1627e0ee14af8d6e4f8 +size 1689 From 5cd98723dc5944f7f3fc98e4996832683b8ce88a Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 18:28:03 -1000 Subject: [PATCH 096/220] Fix handling of case where default image isn't animated --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 22 +++++++++++--------- src/ImageSharp/Formats/Png/PngMetadata.cs | 5 +++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 23e3033dc..222fe8ed3 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -234,8 +234,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals PngThrowHelper.ThrowMissingFrameControl(); } - previousFrameControl ??= new((uint)this.header.Width, (uint)this.header.Height); - this.InitializeFrame(previousFrameControl.Value, currentFrameControl.Value, image, previousFrame, out currentFrame); + this.InitializeFrame(previousFrameControl, currentFrameControl.Value, image, previousFrame, out currentFrame); this.currentStream.Position += 4; this.ReadScanlines( @@ -255,7 +254,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals break; case PngChunkType.Data: - + pngMetadata.DefaultImageAnimated = currentFrameControl != null; currentFrameControl ??= new((uint)this.header.Width, (uint)this.header.Height); if (image is null) { @@ -272,9 +271,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.ReadNextDataChunk, currentFrameControl.Value, cancellationToken); + if (pngMetadata.DefaultImageAnimated) + { + previousFrame = currentFrame; + previousFrameControl = currentFrameControl; + } - previousFrame = currentFrame; - previousFrameControl = currentFrameControl; break; case PngChunkType.Palette: this.palette = chunk.Data.GetSpan().ToArray(); @@ -643,7 +645,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The previous frame. /// The created frame private void InitializeFrame( - FrameControl previousFrameControl, + FrameControl? previousFrameControl, FrameControl currentFrameControl, Image image, ImageFrame? previousFrame, @@ -652,15 +654,15 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame); - // if restoring to before first frame, restore to background - if (previousFrame is null && previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToPrevious) + // If restoring to before first frame, restore to background. Same if first frame (previousFrameControl null). + if (previousFrameControl == null || (previousFrame is null && previousFrameControl.Value.DisposeOperation == PngDisposalMethod.RestoreToPrevious)) { Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(); pixelRegion.Clear(); } - else if (previousFrameControl.DisposeOperation == PngDisposalMethod.RestoreToBackground) + else if (previousFrameControl.Value.DisposeOperation == PngDisposalMethod.RestoreToBackground) { - Rectangle restoreArea = previousFrameControl.Bounds; + Rectangle restoreArea = previousFrameControl.Value.Bounds; Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(restoreArea); pixelRegion.Clear(); } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index 93ddcf263..c4ff3bbe2 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -83,6 +83,11 @@ public class PngMetadata : IDeepCloneable /// public uint RepeatCount { get; set; } = 1; + /// + /// Gets or sets a value indicating whether the default image is shown as part of the animated sequence + /// + public bool DefaultImageAnimated { get; set; } + /// public IDeepCloneable DeepClone() => new PngMetadata(this); From c23283508205a37e15baad4b55bd83ea5e790138 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 19:23:35 -1000 Subject: [PATCH 097/220] Fix PngMetadata copy --- src/ImageSharp/Formats/Png/PngMetadata.cs | 3 ++- tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index c4ff3bbe2..766377f7c 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -29,6 +29,7 @@ public class PngMetadata : IDeepCloneable this.InterlaceMethod = other.InterlaceMethod; this.TransparentColor = other.TransparentColor; this.RepeatCount = other.RepeatCount; + this.DefaultImageAnimated = other.DefaultImageAnimated; if (other.ColorTable?.Length > 0) { @@ -86,7 +87,7 @@ public class PngMetadata : IDeepCloneable /// /// Gets or sets a value indicating whether the default image is shown as part of the animated sequence /// - public bool DefaultImageAnimated { get; set; } + public bool DefaultImageAnimated { get; set; } = true; /// public IDeepCloneable DeepClone() => new PngMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index b3c122a7a..4f9ba9abe 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -32,7 +32,8 @@ public class PngMetadataTests InterlaceMethod = PngInterlaceMode.Adam7, Gamma = 2, TextData = new List { new PngTextData("name", "value", "foo", "bar") }, - RepeatCount = 123 + RepeatCount = 123, + DefaultImageAnimated = false }; PngMetadata clone = (PngMetadata)meta.DeepClone(); @@ -44,6 +45,7 @@ public class PngMetadataTests Assert.False(meta.TextData.Equals(clone.TextData)); Assert.True(meta.TextData.SequenceEqual(clone.TextData)); Assert.True(meta.RepeatCount == clone.RepeatCount); + Assert.True(meta.DefaultImageAnimated == clone.DefaultImageAnimated); clone.BitDepth = PngBitDepth.Bit2; clone.ColorType = PngColorType.Palette; From 5cd2d290526942a9f4a60dcc12fc8db03f7aa194 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Thu, 28 Mar 2024 20:11:43 -1000 Subject: [PATCH 098/220] Make PngEncoder respect DefaultImageAnimated --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 43 +++++++++++++------ .../Formats/Png/PngEncoderTests.cs | 23 ++++++++++ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 113fef595..078935306 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -167,6 +167,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable ImageFrame? clonedFrame = null; ImageFrame currentFrame = image.Frames.RootFrame; + int currentFrameIndex = 0; bool clearTransparency = this.encoder.TransparentColorMode is PngTransparentColorMode.Clear; if (clearTransparency) @@ -196,28 +197,49 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable if (image.Frames.Count > 1) { this.WriteAnimationControlChunk(stream, (uint)image.Frames.Count, pngMetadata.RepeatCount); + } + + // If the first frame isn't animated, write it as usual and skip it when writing animated frames + if (!pngMetadata.DefaultImageAnimated || image.Frames.Count == 1) + { + FrameControl frameControl = new((uint)this.width, (uint)this.height); + this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); + currentFrameIndex++; + } - // Write the first frame. + if (image.Frames.Count > 1) + { + // Write the first animated frame. + currentFrame = image.Frames[currentFrameIndex]; PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame); PngDisposalMethod previousDisposal = frameMetadata.DisposalMethod; FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); - this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); + uint sequenceNumber = 1; + if (pngMetadata.DefaultImageAnimated) + { + this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); + } + else + { + sequenceNumber += this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, true); + } + + currentFrameIndex++; // Capture the global palette for reuse on subsequent frames. ReadOnlyMemory? previousPalette = quantized?.Palette.ToArray(); // Write following frames. - uint increment = 0; ImageFrame previousFrame = image.Frames.RootFrame; // This frame is reused to store de-duplicated pixel buffers. using ImageFrame encodingFrame = new(image.Configuration, previousFrame.Size()); - for (int i = 1; i < image.Frames.Count; i++) + for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++) { ImageFrame? prev = previousDisposal == PngDisposalMethod.RestoreToBackground ? null : previousFrame; - currentFrame = image.Frames[i]; - ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; + currentFrame = image.Frames[currentFrameIndex]; + ImageFrame? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null; frameMetadata = GetPngFrameMetadata(currentFrame); bool blend = frameMetadata.BlendMethod == PngBlendMethod.Over; @@ -238,22 +260,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable } // Each frame control sequence number must be incremented by the number of frame data chunks that follow. - frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, (uint)i + increment); + frameControl = this.WriteFrameControlChunk(stream, frameMetadata, bounds, sequenceNumber); // Dispose of previous quantized frame and reassign. quantized?.Dispose(); quantized = this.CreateQuantizedImageAndUpdateBitDepth(pngMetadata, encodingFrame, bounds, previousPalette); - increment += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true); + sequenceNumber += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true) + 1; previousFrame = currentFrame; previousDisposal = frameMetadata.DisposalMethod; } } - else - { - FrameControl frameControl = new((uint)this.width, (uint)this.height); - this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); - } this.WriteEndChunk(stream); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index a70fb86df..e7884dd58 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -587,6 +587,29 @@ public partial class PngEncoderTests } } + [Theory] + [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] + public void Encode_DefaultNotAnimated(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + using MemoryStream memStream = new(); + image.Save(memStream, PngEncoder); + memStream.Position = 0; + + image.DebugSave(provider: provider, encoder: PngEncoder, null, false); + + using Image output = Image.Load(memStream); + ImageComparer.Exact.VerifySimilarity(output, image); + + Assert.Equal(2, image.Frames.Count); + Assert.Equal(image.Frames.Count, output.Frames.Count); + + PngMetadata originalMetadata = image.Metadata.GetPngMetadata(); + PngMetadata outputMetadata = output.Metadata.GetPngMetadata(); + Assert.Equal(originalMetadata.DefaultImageAnimated, outputMetadata.DefaultImageAnimated); + } + [Theory] [MemberData(nameof(PngTrnsFiles))] public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) From c5ebbfe76e84f904750199e0fa3482ce67111eca Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Fri, 29 Mar 2024 21:34:48 -1000 Subject: [PATCH 099/220] Add more tests --- .../Formats/Png/PngEncoderTests.Chunks.cs | 33 +++++++++++++++++++ .../Formats/Png/PngMetadataTests.cs | 20 +++++++++++ 2 files changed, 53 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs index 044da2193..2e3390298 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs @@ -3,6 +3,7 @@ using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -59,6 +60,38 @@ public partial class PngEncoderTests } } + [Theory] + [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] + public void AcTL_CorrectlyWritten(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + PngMetadata metadata = image.Metadata.GetPngMetadata(); + int correctFrameCount = image.Frames.Count - (metadata.DefaultImageAnimated ? 0 : 1); + using MemoryStream memStream = new(); + image.Save(memStream, PngEncoder); + memStream.Position = 0; + Span bytesSpan = memStream.ToArray().AsSpan(8); // Skip header. + bool foundAcTl = false; + while (bytesSpan.Length > 0 && !foundAcTl) + { + int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan[..4]); + PngChunkType type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + if (type == PngChunkType.AnimationControl) + { + AnimationControl control = AnimationControl.Parse(bytesSpan[8..]); + foundAcTl = true; + Assert.True(control.NumberFrames == correctFrameCount); + Assert.True(control.NumberPlays == metadata.RepeatCount); + } + + bytesSpan = bytesSpan[(4 + 4 + length + 4)..]; + } + + Assert.True(foundAcTl); + } + [Theory] [InlineData(PngChunkType.Gamma)] [InlineData(PngChunkType.Chroma)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 4f9ba9abe..8308935d0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -146,6 +146,26 @@ public class PngMetadataTests VerifyExifDataIsPresent(exif); } + [Theory] + [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] + public void Decode_IdentifiesDefaultFrameNotAnimated(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + Assert.False(meta.DefaultImageAnimated); + } + + [Theory] + [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] + public void Decode_IdentifiesDefaultFrameAnimated(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + Assert.True(meta.DefaultImageAnimated); + } + [Theory] [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] public void Decode_IgnoresExifData_WhenIgnoreMetadataIsTrue(TestImageProvider provider) From 61b5b0c3a943816ae1146b1a0a6ded4e7a429075 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Fri, 29 Mar 2024 21:35:53 -1000 Subject: [PATCH 100/220] Fix incorrect acTL frame count --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 078935306..99f721fcf 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -196,7 +196,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable if (image.Frames.Count > 1) { - this.WriteAnimationControlChunk(stream, (uint)image.Frames.Count, pngMetadata.RepeatCount); + this.WriteAnimationControlChunk(stream, (uint)(image.Frames.Count - (pngMetadata.DefaultImageAnimated ? 0 : 1)), pngMetadata.RepeatCount); } // If the first frame isn't animated, write it as usual and skip it when writing animated frames From 5cd96d3bf007a7816691f6b9c2c843118d95a884 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Mon, 1 Apr 2024 17:03:25 -0700 Subject: [PATCH 101/220] re-add comment --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 222fe8ed3..77edf2d9a 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -654,7 +654,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame); - // If restoring to before first frame, restore to background. Same if first frame (previousFrameControl null). + // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND. + // So, if restoring to before first frame, clear entire area. Same if first frame (previousFrameControl null). if (previousFrameControl == null || (previousFrame is null && previousFrameControl.Value.DisposeOperation == PngDisposalMethod.RestoreToPrevious)) { Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(); From 3dec79c9797ad8d97740924a26980d5ae359ab40 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Mon, 1 Apr 2024 17:28:27 -0700 Subject: [PATCH 102/220] Add test for case with frame offsets --- .../Formats/Png/PngEncoderTests.cs | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index e7884dd58..595adbadc 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -448,6 +448,8 @@ public partial class PngEncoderTests [Theory] [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.FrameOffset, PixelTypes.Rgba32)] public void Encode_APng(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -459,15 +461,17 @@ public partial class PngEncoderTests image.DebugSave(provider: provider, encoder: PngEncoder, null, false); using Image output = Image.Load(memStream); - ImageComparer.Exact.VerifySimilarity(output, image); - Assert.Equal(5, image.Frames.Count); + // some loss from original, due to compositing + ImageComparer.TolerantPercentage(0.01f).VerifySimilarity(output, image); + Assert.Equal(image.Frames.Count, output.Frames.Count); PngMetadata originalMetadata = image.Metadata.GetPngMetadata(); PngMetadata outputMetadata = output.Metadata.GetPngMetadata(); Assert.Equal(originalMetadata.RepeatCount, outputMetadata.RepeatCount); + Assert.Equal(originalMetadata.DefaultImageAnimated, outputMetadata.DefaultImageAnimated); for (int i = 0; i < image.Frames.Count; i++) { @@ -587,29 +591,6 @@ public partial class PngEncoderTests } } - [Theory] - [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] - public void Encode_DefaultNotAnimated(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(PngDecoder.Instance); - using MemoryStream memStream = new(); - image.Save(memStream, PngEncoder); - memStream.Position = 0; - - image.DebugSave(provider: provider, encoder: PngEncoder, null, false); - - using Image output = Image.Load(memStream); - ImageComparer.Exact.VerifySimilarity(output, image); - - Assert.Equal(2, image.Frames.Count); - Assert.Equal(image.Frames.Count, output.Frames.Count); - - PngMetadata originalMetadata = image.Metadata.GetPngMetadata(); - PngMetadata outputMetadata = output.Metadata.GetPngMetadata(); - Assert.Equal(originalMetadata.DefaultImageAnimated, outputMetadata.DefaultImageAnimated); - } - [Theory] [MemberData(nameof(PngTrnsFiles))] public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) From c31db731ad0b1f070aeb28a0640bba5519faf7b4 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Mon, 1 Apr 2024 17:56:21 -0700 Subject: [PATCH 103/220] re-add rest of comment --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 77edf2d9a..61074a26e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -652,6 +652,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals out ImageFrame frame) where TPixel : unmanaged, IPixel { + // We create a clone of the previous frame and add it. + // We will overpaint the difference of pixels on the current frame to create a complete image. + // This ensures that we have enough pixel data to process without distortion. #2450 frame = image.Frames.AddFrame(previousFrame ?? image.Frames.RootFrame); // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND. From 07e354dca1a3aee0d429e0b5e7fd20f84a933e22 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 2 Apr 2024 21:26:53 +1000 Subject: [PATCH 104/220] Add YCbCr conversion --- ...olorProfileConverterExtensionsCieXyzRgb.cs | 52 ++++++ ...olorProfileConverterExtensionsRgbCieXyz.cs | 52 ++++++ src/ImageSharp/ColorProfiles/Rgb.cs | 5 +- src/ImageSharp/ColorProfiles/YCbCr.cs | 158 ++++++++++++++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../RgbAndYCbCrConversionTest.cs | 76 +++++++++ 6 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/YCbCr.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs new file mode 100644 index 000000000..b0ec427c5 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs new file mode 100644 index 000000000..f98d4bfc3 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFrom = source.ToProfileConnectingSpace(options); + + // Convert between PCS + CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index bccae1132..21575dd85 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -7,7 +7,10 @@ using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; namespace SixLabors.ImageSharp.ColorProfiles; -internal readonly struct Rgb : IProfileConnectingSpace +/// +/// Represents an RGB (red, green, blue) color profile. +/// +public readonly struct Rgb : IProfileConnectingSpace { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs new file mode 100644 index 000000000..07062b32a --- /dev/null +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -0,0 +1,158 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. +/// +/// +/// +public readonly struct YCbCr : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(255); + + /// + /// Initializes a new instance of the struct. + /// + /// The y luminance component. + /// The cb chroma component. + /// The cr chroma component. + [MethodImpl(InliningOptions.ShortMethod)] + public YCbCr(float y, float cb, float cr) + : this(new Vector3(y, cb, cr)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the y, cb, cr components. + [MethodImpl(InliningOptions.ShortMethod)] + public YCbCr(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.Y = vector.X; + this.Cb = vector.Y; + this.Cr = vector.Z; + } + + /// + /// Gets the Y luminance component. + /// A value ranging between 0 and 255. + /// + public readonly float Y { get; } + + /// + /// Gets the Cb chroma component. + /// A value ranging between 0 and 255. + /// + public readonly float Cb { get; } + + /// + /// Gets the Cr chroma component. + /// A value ranging between 0 and 255. + /// + public readonly float Cr { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); + + /// + public static YCbCr FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + Vector3 rgb = source.ToScaledVector3() * Max; + float r = rgb.X; + float g = rgb.Y; + float b = rgb.Z; + + float y = (0.299F * r) + (0.587F * g) + (0.114F * b); + float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); + float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); + + return new YCbCr(y, cb, cr); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float y = this.Y; + float cb = this.Cb - 128F; + float cr = this.Cr - 128F; + + float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); + float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); + float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + + return Rgb.FromScaledVector3(new Vector3(r, g, b) / Max); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + YCbCr ycbcr = source[i]; + destination[i] = ycbcr.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); + + /// + public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})"); + + /// + public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(YCbCr other) + => this.Y.Equals(other.Y) + && this.Cb.Equals(other.Cb) + && this.Cr.Equals(other.Cr); +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 84319b66c..921148318 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -14,7 +14,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -34,6 +35,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Rgb x, Rgb y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + public bool Equals(YCbCr x, YCbCr y) => this.Equals(x.Y, y.Y) && this.Equals(x.Cb, y.Cb) && this.Equals(x.Cr, y.Cr); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -44,6 +47,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] Rgb obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] YCbCr obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs new file mode 100644 index 000000000..05c17e89e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated mathematically +/// +public class RgbAndYCbCrConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.001F); + + [Theory] + [InlineData(255, 128, 128, 1, 1, 1)] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(128, 128, 128, 0.502, 0.502, 0.502)] + public void Convert_YCbCr_To_Rgb(float y, float cb, float cr, float r, float g, float b) + { + // Arrange + YCbCr input = new(y, cb, cr); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(1, 1, 1, 255, 128, 128)] + [InlineData(0.5, 0.5, 0.5, 127.5, 128, 128)] + [InlineData(1, 0, 0, 76.245, 84.972, 255)] + public void Convert_Rgb_To_YCbCr(float r, float g, float b, float y, float cb, float cr) + { + // Arrange + Rgb input = new(r, g, b); + YCbCr expected = new(y, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 94d7f3c479ed2ecf7e831f19da1bdac67b6d7398 Mon Sep 17 00:00:00 2001 From: SpaceCheetah Date: Tue, 2 Apr 2024 23:00:15 -0700 Subject: [PATCH 105/220] Rename DefaultImageAnimated to AnimateRootFrame --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 4 ++-- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 6 +++--- src/ImageSharp/Formats/Png/PngMetadata.cs | 6 +++--- .../Formats/Png/PngEncoderTests.Chunks.cs | 2 +- tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 61074a26e..9cf88f729 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -254,7 +254,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals break; case PngChunkType.Data: - pngMetadata.DefaultImageAnimated = currentFrameControl != null; + pngMetadata.AnimateRootFrame = currentFrameControl != null; currentFrameControl ??= new((uint)this.header.Width, (uint)this.header.Height); if (image is null) { @@ -271,7 +271,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.ReadNextDataChunk, currentFrameControl.Value, cancellationToken); - if (pngMetadata.DefaultImageAnimated) + if (pngMetadata.AnimateRootFrame) { previousFrame = currentFrame; previousFrameControl = currentFrameControl; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 99f721fcf..6e8224f01 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -196,11 +196,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable if (image.Frames.Count > 1) { - this.WriteAnimationControlChunk(stream, (uint)(image.Frames.Count - (pngMetadata.DefaultImageAnimated ? 0 : 1)), pngMetadata.RepeatCount); + this.WriteAnimationControlChunk(stream, (uint)(image.Frames.Count - (pngMetadata.AnimateRootFrame ? 0 : 1)), pngMetadata.RepeatCount); } // If the first frame isn't animated, write it as usual and skip it when writing animated frames - if (!pngMetadata.DefaultImageAnimated || image.Frames.Count == 1) + if (!pngMetadata.AnimateRootFrame || image.Frames.Count == 1) { FrameControl frameControl = new((uint)this.width, (uint)this.height); this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); @@ -215,7 +215,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable PngDisposalMethod previousDisposal = frameMetadata.DisposalMethod; FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); uint sequenceNumber = 1; - if (pngMetadata.DefaultImageAnimated) + if (pngMetadata.AnimateRootFrame) { this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false); } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index 766377f7c..d9028dd80 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -29,7 +29,7 @@ public class PngMetadata : IDeepCloneable this.InterlaceMethod = other.InterlaceMethod; this.TransparentColor = other.TransparentColor; this.RepeatCount = other.RepeatCount; - this.DefaultImageAnimated = other.DefaultImageAnimated; + this.AnimateRootFrame = other.AnimateRootFrame; if (other.ColorTable?.Length > 0) { @@ -85,9 +85,9 @@ public class PngMetadata : IDeepCloneable public uint RepeatCount { get; set; } = 1; /// - /// Gets or sets a value indicating whether the default image is shown as part of the animated sequence + /// Gets or sets a value indicating whether the root frame is shown as part of the animated sequence /// - public bool DefaultImageAnimated { get; set; } = true; + public bool AnimateRootFrame { get; set; } = true; /// public IDeepCloneable DeepClone() => new PngMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs index 2e3390298..76fd260dd 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs @@ -68,7 +68,7 @@ public partial class PngEncoderTests { using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata metadata = image.Metadata.GetPngMetadata(); - int correctFrameCount = image.Frames.Count - (metadata.DefaultImageAnimated ? 0 : 1); + int correctFrameCount = image.Frames.Count - (metadata.AnimateRootFrame ? 0 : 1); using MemoryStream memStream = new(); image.Save(memStream, PngEncoder); memStream.Position = 0; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 595adbadc..35c446c70 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -471,7 +471,7 @@ public partial class PngEncoderTests PngMetadata outputMetadata = output.Metadata.GetPngMetadata(); Assert.Equal(originalMetadata.RepeatCount, outputMetadata.RepeatCount); - Assert.Equal(originalMetadata.DefaultImageAnimated, outputMetadata.DefaultImageAnimated); + Assert.Equal(originalMetadata.AnimateRootFrame, outputMetadata.AnimateRootFrame); for (int i = 0; i < image.Frames.Count; i++) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 8308935d0..225e4deef 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -33,7 +33,7 @@ public class PngMetadataTests Gamma = 2, TextData = new List { new PngTextData("name", "value", "foo", "bar") }, RepeatCount = 123, - DefaultImageAnimated = false + AnimateRootFrame = false }; PngMetadata clone = (PngMetadata)meta.DeepClone(); @@ -45,7 +45,7 @@ public class PngMetadataTests Assert.False(meta.TextData.Equals(clone.TextData)); Assert.True(meta.TextData.SequenceEqual(clone.TextData)); Assert.True(meta.RepeatCount == clone.RepeatCount); - Assert.True(meta.DefaultImageAnimated == clone.DefaultImageAnimated); + Assert.True(meta.AnimateRootFrame == clone.AnimateRootFrame); clone.BitDepth = PngBitDepth.Bit2; clone.ColorType = PngColorType.Palette; @@ -153,7 +153,7 @@ public class PngMetadataTests { using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.False(meta.DefaultImageAnimated); + Assert.False(meta.AnimateRootFrame); } [Theory] @@ -163,7 +163,7 @@ public class PngMetadataTests { using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.True(meta.DefaultImageAnimated); + Assert.True(meta.AnimateRootFrame); } [Theory] From e24146b0495ddd51b2c0ffd673c6cbf96d5f2a58 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 12:10:33 +1000 Subject: [PATCH 106/220] Add Add LUV and LCHLUV --- src/ImageSharp/ColorProfiles/CieConstants.cs | 4 +- src/ImageSharp/ColorProfiles/CieLchuv.cs | 191 ++++++++++++++++ src/ImageSharp/ColorProfiles/CieLuv.cs | 211 ++++++++++++++++++ src/ImageSharp/ColorProfiles/YCbCr.cs | 10 +- src/ImageSharp/Common/Helpers/Numerics.cs | 8 + .../ApproximateColorProfileComparer.cs | 12 +- .../CieLchuvAndCieLuvConversionTests.cs | 89 ++++++++ 7 files changed, 517 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieLchuv.cs create mode 100644 src/ImageSharp/ColorProfiles/CieLuv.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieConstants.cs b/src/ImageSharp/ColorProfiles/CieConstants.cs index f4b74eaa1..d13a84450 100644 --- a/src/ImageSharp/ColorProfiles/CieConstants.cs +++ b/src/ImageSharp/ColorProfiles/CieConstants.cs @@ -12,10 +12,10 @@ internal static class CieConstants /// /// 216F / 24389F /// - public const float Epsilon = 0.008856452F; + public const float Epsilon = 216f / 24389f; /// /// 24389F / 27F /// - public const float Kappa = 903.2963F; + public const float Kappa = 24389f / 27f; } diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs new file mode 100644 index 000000000..f8f62d104 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -0,0 +1,191 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. +/// +/// +public readonly struct CieLchuv : IColorProfile +{ + private static readonly Vector3 Min = new(0, -200, 0); + private static readonly Vector3 Max = new(100, 200, 360); + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The chroma, relative saturation. + /// The hue in degrees. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(float l, float c, float h) + : this(new Vector3(l, c, h)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, c, h components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(Vector3 vector) + : this() + { + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C { get; } + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. + /// + public readonly float H { get; } + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right); + + /// + public static CieLchuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + CieLuv luv = CieLuv.FromProfileConnectingSpace(options, source); + + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = luv.L, u = luv.U, v = luv.V; + float c = MathF.Sqrt((u * u) + (v * v)); + float hRadians = MathF.Atan2(v, u); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); + + // Wrap the angle round at 360. + hDegrees %= 360; + + // Make sure it's not negative. + while (hDegrees < 0) + { + hDegrees += 360; + } + + return new CieLchuv(l, c, hDegrees); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = this.L, c = this.C, hDegrees = this.H; + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); + + float u = c * MathF.Cos(hRadians); + float v = c * MathF.Sin(hRadians); + + CieLuv luv = new(l, u, v); + return luv.ToProfileConnectingSpace(options); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieLchuv lch = source[i]; + destination[i] = lch.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); + + /// + public override string ToString() + => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is CieLchuv other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLchuv other) + => this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H) + && this.WhitePoint.Equals(other.WhitePoint); + + /// + /// Computes the saturation of the color (chroma normalized by lightness) + /// + /// + /// A value ranging from 0 to 100. + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Saturation() + { + float result = 100 * (this.C / this.L); + + if (float.IsNaN(result)) + { + return 0; + } + + return result; + } +} diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs new file mode 100644 index 000000000..8708ea255 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -0,0 +1,211 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International +/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which +/// attempted perceptual uniformity +/// +/// +public readonly struct CieLuv : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The blue-yellow chromaticity coordinate of the given white point. + /// The red-green chromaticity coordinate of the given white point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLuv(float l, float u, float v) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = l; + this.U = u; + this.V = v; + } + + /// + /// Gets the lightness dimension + /// A value usually ranging between 0 and 100. + /// + public readonly float L { get; } + + /// + /// Gets the blue-yellow chromaticity coordinate of the given white point. + /// A value usually ranging between -100 and 100. + /// + public readonly float U { get; } + + /// + /// Gets the red-green chromaticity coordinate of the given white point. + /// A value usually ranging between -100 and 100. + /// + public readonly float V { get; } + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right); + + /// + public static CieLuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Use doubles here for accuracy. + // Conversion algorithm described here: + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html + CieXyz whitePoint = options.TargetWhitePoint; + + double yr = source.Y / whitePoint.Y; + + double den = source.X + (15 * source.Y) + (3 * source.Z); + double up = den > 0 ? ComputeU(in source) : 0; + double vp = den > 0 ? ComputeV(in source) : 0; + double upr = ComputeU(in whitePoint); + double vpr = ComputeV(in whitePoint); + + const double e = 1 / 3d; + double l = yr > CieConstants.Epsilon + ? ((116 * Math.Pow(yr, e)) - 16d) + : (CieConstants.Kappa * yr); + + if (double.IsNaN(l)) + { + l = 0; + } + + double u = 13 * l * (up - upr); + double v = 13 * l * (vp - vpr); + + if (double.IsNaN(u) || u == -0d) + { + u = 0; + } + + if (double.IsNaN(v) || u == -0d) + { + v = 0; + } + + return new CieLuv((float)l, (float)u, (float)v); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Use doubles here for accuracy. + // Conversion algorithm described here: + // http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html + CieXyz whitePoint = options.WhitePoint; + + double l = this.L, u = this.U, v = this.V; + + double u0 = ComputeU(in whitePoint); + double v0 = ComputeV(in whitePoint); + + double y = l > CieConstants.Kappa * CieConstants.Epsilon + ? Numerics.Pow3((l + 16) / 116d) + : l / CieConstants.Kappa; + + double a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; + double b = -5 * y; + const double c = -1 / 3d; + double d = y * ((39 * l / (v + (13 * l * v0))) - 5); + + double x = (d - b) / (a - c); + double z = (x * a) + b; + + if (double.IsNaN(x)) + { + x = 0; + } + + if (double.IsNaN(y)) + { + y = 0; + } + + if (double.IsNaN(z)) + { + z = 0; + } + + return new CieXyz((float)x, (float)y, (float)z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieLuv luv = source[i]; + destination[i] = luv.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); + + /// + public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLuv other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLuv other) + => this.L.Equals(other.L) + && this.U.Equals(other.U) + && this.V.Equals(other.V) + && this.WhitePoint.Equals(other.WhitePoint); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double ComputeU(in CieXyz source) + => (4 * source.X) / (source.X + (15 * source.Y) + (3 * source.Z)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double ComputeV(in CieXyz source) + => (9 * source.Y) / (source.X + (15 * source.Y) + (3 * source.Z)); +} diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index 07062b32a..5f0b4118a 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -22,7 +22,7 @@ public readonly struct YCbCr : IColorProfile /// The y luminance component. /// The cb chroma component. /// The cr chroma component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(float y, float cb, float cr) : this(new Vector3(y, cb, cr)) { @@ -32,7 +32,7 @@ public readonly struct YCbCr : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the y, cb, cr components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); @@ -77,7 +77,7 @@ public readonly struct YCbCr : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); /// @@ -140,7 +140,7 @@ public readonly struct YCbCr : IColorProfile => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); /// @@ -150,7 +150,7 @@ public readonly struct YCbCr : IColorProfile public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) => this.Y.Equals(other.Y) && this.Cb.Equals(other.Cb) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 777de2dc9..ca14ae4c3 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -141,6 +141,14 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Pow3(float x) => x * x * x; + /// + /// Returns a specified number raised to the power of 3 + /// + /// A double-precision floating-point number + /// The number raised to the power of 3. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Pow3(double x) => x * x * x; + /// /// Implementation of 1D Gaussian G(x) function /// diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 921148318..f2c1d20a3 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -15,7 +15,9 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -37,6 +39,10 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(YCbCr x, YCbCr y) => this.Equals(x.Y, y.Y) && this.Equals(x.Cb, y.Cb) && this.Equals(x.Cr, y.Cr); + public bool Equals(CieLchuv x, CieLchuv y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + + public bool Equals(CieLuv x, CieLuv y) => this.Equals(x.L, y.L) && this.Equals(x.U, y.U) && this.Equals(x.V, y.V); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -49,6 +55,10 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] YCbCr obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieLchuv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieLuv obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs new file mode 100644 index 000000000..a65c7e81c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLchuvAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, 50, 180, 100, -50, 0)] + [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] + [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] + [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] + [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] + public void Convert_CieLchuv_to_CieLuv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, -50, 0, 100, 50, 180)] + [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] + [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] + [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] + [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] + [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] + public void Convert_CieLuv_to_CieLchuv(float l, float u, float v, float l2, float c, float h) + { + // Arrange + CieLuv input = new(l, u, v); + CieLchuv expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 7da1cae8de912d45621fde7f834d49726cbc4ab7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 13:15:34 +1000 Subject: [PATCH 107/220] Add XYY --- src/ImageSharp/ColorProfiles/CieXyy.cs | 156 ++++++++++++++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieXyzAndCieXyyConversionTest.cs | 76 +++++++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/ColorProfiles/CieXyy.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs new file mode 100644 index 000000000..9a1953ac8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -0,0 +1,156 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CIE xyY 1931 color +/// +/// +public readonly struct CieXyy : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The x chroma component. + /// The y chroma component. + /// The y luminance component. + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyy(float x, float y, float yl) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = x; + this.Y = y; + this.Yl = yl; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the x, y, Y components. + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyy(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Yl = vector.Z; + } + + /// + /// Gets the X chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float X { get; } + + /// + /// Gets the Y chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float Y { get; } + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float Yl { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); + + /// + public static CieXyy FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + float x = source.X / (source.X + source.Y + source.Z); + float y = source.Y / (source.X + source.Y + source.Z); + + if (float.IsNaN(x) || float.IsNaN(y)) + { + return new CieXyy(0, 0, source.Y); + } + + return new CieXyy(x, y, source.Y); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + if (MathF.Abs(this.Y) < Constants.Epsilon) + { + return new CieXyz(0, 0, this.Yl); + } + + float x = (this.X * this.Yl) / this.Y; + float y = this.Yl; + float z = ((1 - this.X - this.Y) * y) / this.Y; + + return new CieXyz(x, y, z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyy xyz = source[i]; + destination[i] = xyz.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.X, this.Y, this.Yl); + + /// + public override string ToString() + => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(CieXyy other) + => this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Yl.Equals(other.Yl); +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index f2c1d20a3..81aac269d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -17,7 +17,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -43,6 +44,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieLuv x, CieLuv y) => this.Equals(x.L, y.L) && this.Equals(x.U, y.U) && this.Equals(x.V, y.V); + public bool Equals(CieXyy x, CieXyy y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Yl, y.Yl); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -59,6 +62,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieLuv obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieXyy obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs new file mode 100644 index 000000000..c4fa554a6 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieXyyConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] + [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] + [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] + [InlineData(0, 0, 0, 0.538842, 0.000000, 0.000000)] + public void Convert_Xyy_To_Xyz(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) + { + CieXyy input = new(x, y, yl); + CieXyz expected = new(xyzX, xyzY, xyzZ); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] + [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] + [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] + [InlineData(0.231809, 0, 0.077528, 0.749374, 0.000000, 0.000000)] + public void Convert_Xyz_to_Xyy(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) + { + CieXyz input = new(xyzX, xyzY, xyzZ); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From b8c73e121d76caf0d3c85b495bc3110b186843f3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 14:23:18 +1000 Subject: [PATCH 108/220] Add CMYK --- src/ImageSharp/ColorProfiles/CieXyy.cs | 10 +- src/ImageSharp/ColorProfiles/Cmyk.cs | 164 ++++++++++++++++++ ...rProfileConverterExtensionsCieLabCieLab.cs | 55 ++++++ .../ColorProfileConverterExtensionsRgbRgb.cs | 55 ++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieLabAndCmykConversionTests.cs | 70 ++++++++ .../CmykAndYCbCrConversionTests.cs | 70 ++++++++ .../ColorProfiles/RgbAndCmykConversionTest.cs | 77 ++++++++ 8 files changed, 502 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/Cmyk.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index 9a1953ac8..4dcb582d6 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -18,7 +18,7 @@ public readonly struct CieXyy : IColorProfile /// The x chroma component. /// The y chroma component. /// The y luminance component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(float x, float y, float yl) { // Not clamping as documentation about this space only indicates "usual" ranges @@ -31,7 +31,7 @@ public readonly struct CieXyy : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the x, y, Y components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(Vector3 vector) : this() { @@ -67,7 +67,7 @@ public readonly struct CieXyy : IColorProfile /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); /// @@ -78,7 +78,7 @@ public readonly struct CieXyy : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); /// @@ -148,7 +148,7 @@ public readonly struct CieXyy : IColorProfile public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs new file mode 100644 index 000000000..d586ab6d4 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -0,0 +1,164 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CMYK (cyan, magenta, yellow, keyline) color. +/// +public readonly struct Cmyk : IColorProfile +{ + private static readonly Vector4 Min = Vector4.Zero; + private static readonly Vector4 Max = Vector4.One; + + /// + /// Initializes a new instance of the struct. + /// + /// The cyan component. + /// The magenta component. + /// The yellow component. + /// The keyline black component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cmyk(float c, float m, float y, float k) + : this(new Vector4(c, m, y, k)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the c, m, y, k components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cmyk(Vector4 vector) + { + vector = Numerics.Clamp(vector, Min, Max); + this.C = vector.X; + this.M = vector.Y; + this.Y = vector.Z; + this.K = vector.W; + } + + /// + /// Gets the cyan color component. + /// A value ranging between 0 and 1. + /// + public readonly float C { get; } + + /// + /// Gets the magenta color component. + /// A value ranging between 0 and 1. + /// + public readonly float M { get; } + + /// + /// Gets the yellow color component. + /// A value ranging between 0 and 1. + /// + public readonly float Y { get; } + + /// + /// Gets the keyline black color component. + /// A value ranging between 0 and 1. + /// + public readonly float K { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right); + + /// + public static Cmyk FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + // To CMY + Vector3 cmy = Vector3.One - source.ToScaledVector3(); + + // To CMYK + Vector3 k = new(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z))); + + if (MathF.Abs(k.X - 1F) < Constants.Epsilon) + { + return new Cmyk(0, 0, 0, 1F); + } + + cmy = (cmy - k) / (Vector3.One - k); + + return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + Vector3 rgb = (Vector3.One - new Vector3(this.C, this.M, this.Y)) * (Vector3.One - new Vector3(this.K)); + return Rgb.FromScaledVector3(rgb); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + // TODO: We can possibly optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Cmyk cmyk = source[i]; + destination[i] = cmyk.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + => HashCode.Combine(this.C, this.M, this.Y, this.K); + + /// + public override string ToString() + => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is Cmyk other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Cmyk other) + => this.C.Equals(other.C) + && this.M.Equals(other.M) + && this.Y.Equals(other.Y) + && this.K.Equals(other.K); +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs new file mode 100644 index 000000000..51c68531d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromTo = pcsFromToOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromTo); + + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + CieLab.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsFromTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs new file mode 100644 index 000000000..248a54026 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromTo = pcsFromToOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromTo); + + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsFromTo, destination); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 81aac269d..f67f47222 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -18,7 +18,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -46,6 +47,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieXyy x, CieXyy y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Yl, y.Yl); + public bool Equals(Cmyk x, Cmyk y) => this.Equals(x.C, y.C) && this.Equals(x.M, y.M) && this.Equals(x.Y, y.Y) && this.Equals(x.K, y.K); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -64,6 +67,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieXyy obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Cmyk obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs new file mode 100644 index 000000000..a13ad7d15 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndCmykConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 1, 0.6156551, 5.960464E-08, 55.063, 82.54871, 23.16506)] + public void Convert_Cmyk_to_CieLab(float c, float m, float y, float k, float l, float a, float b) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 303.6901, 10.01514, 0, 1, 0.597665966, 0)] + public void Convert_CieLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) + { + // Arrange + CieLab input = new(l, a, b); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs new file mode 100644 index 000000000..ef3305ec7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CmykAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 255, 128, 128)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] + public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) + { + // Arrange + Cmyk input = new(c, m, y, k); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] + [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] + public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) + { + // Arrange + YCbCr input = new(y2, cb, cr); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs new file mode 100644 index 000000000..3ae2996f2 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +/// +public class RgbAndCmykConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(1, 1, 1, 1, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 1, 1, 1)] + [InlineData(0, 0.84, 0.037, 0.365, 0.635, 0.1016, 0.6115)] + public void Convert_Cmyk_To_Rgb(float c, float m, float y, float k, float r, float g, float b) + { + // Arrange + Cmyk input = new(c, m, y, k); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(0.635, 0.1016, 0.6115, 0, 0.84, 0.037, 0.365)] + public void Convert_Rgb_To_Cmyk(float r, float g, float b, float c, float m, float y, float k) + { + // Arrange + Rgb input = new(r, g, b); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 55de3ccc52dfd1fd30969a852db03aecffb461c6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 16:06:13 +1000 Subject: [PATCH 109/220] Add more LAB tests --- .../CieLabAndCieLchuvConversionTests.cs | 76 +++++++++++++++++++ .../CieLabAndCieLuvConversionTests.cs | 76 +++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs new file mode 100644 index 000000000..d7f2df95b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLchuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] + public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 29.4713573, 200, 352.6346)] + public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) + { + // Arrange + CieLab input = new(l, a, b); + CieLchuv expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs new file mode 100644 index 000000000..17df78bdf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10, 36.0555, 303.6901, 10.6006718, -17.24077, 82.8835)] + public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + CieLuv input = new(l, u, v); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] + public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) + { + // Arrange + CieLab input = new(l, a, b); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 51c3094799041e53478e0a6fbbfc7f9575873c89 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 16:52:01 +1000 Subject: [PATCH 110/220] More LAB tests --- .../CieLabAndCieLchuvConversionTests.cs | 4 +- .../CieLabAndCieLuvConversionTests.cs | 4 +- .../CieLabAndCieXyyConversionTests.cs | 70 +++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index d7f2df95b..7bf7b4231 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -19,7 +19,7 @@ public class CieLabAndCieLchuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] - public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) + public void Convert_Lchuv_To_Lab(float l, float c, float h, float l2, float a, float b) { // Arrange CieLchuv input = new(l, c, h); @@ -48,7 +48,7 @@ public class CieLabAndCieLchuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(36.0555, 303.6901, 10.01514, 29.4713573, 200, 352.6346)] - public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) + public void Convert_Lab_To_Lchuv(float l, float a, float b, float l2, float c, float h) { // Arrange CieLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index 17df78bdf..fb9f25bd8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -19,7 +19,7 @@ public class CieLabAndCieLuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(10, 36.0555, 303.6901, 10.6006718, -17.24077, 82.8835)] - public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) + public void Convert_CieLuv_To_CieLab(float l, float u, float v, float l2, float a, float b) { // Arrange CieLuv input = new(l, u, v); @@ -48,7 +48,7 @@ public class CieLabAndCieLuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] - public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) + public void Convert_CieLab_To_CieLuv(float l, float a, float b, float l2, float u, float v) { // Arrange CieLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs new file mode 100644 index 000000000..074173da5 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8644734, 0.06098868, 0.06509002, 30.6619, 291.5721, -11.2526)] + public void Convert_CieXyy_To_CieLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.6619, 291.5721, -11.2526, 0.8644734, 0.06098868, 0.06509002)] + public void Convert_CieLab_To_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + CieLab input = new(l, a, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 611c78dcd7bf4d83bbcb4bc0bdcd9711538c4e25 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 21:12:05 +1000 Subject: [PATCH 111/220] More tests --- .../CieLabAndRgbConversionTests.cs | 71 +++++++++++++++++++ .../CieLchuvAndCieLchConversionTests.cs | 71 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs new file mode 100644 index 000000000..3b0b2f96a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + private static readonly ColorProfileConverter Converter = new(); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.9999999, 0, 0.384345, 55.063, 82.54871, 23.16505)] + public void Convert_Rgb_to_CieLab(float r, float g, float b2, float l, float a, float b) + { + // Arrange + Rgb input = new(r, g, b2); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54871, 23.16505, 0.9999999, 0, 0.384345)] + public void Convert_CieLab_to_Rgb(float l, float a, float b, float r, float g, float b2) + { + // Arrange + CieLab input = new(l, a, b); + Rgb expected = new(r, g, b2); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs new file mode 100644 index 000000000..580e66e5e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLchuvAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.73742, 64.79149, 30.1786, 36.0555, 103.6901, 10.01513)] + public void Convert_CieLch_To_CieLchuv(float l2, float c2, float h2, float l, float c, float h) + { + // Arrange + CieLch input = new(l2, c2, h2); + CieLchuv expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(36.0555, 103.6901, 10.01514, 36.73742, 64.79149, 30.1786)] + public void Convert_CieLchuv_To_CieLch(float l, float c, float h, float l2, float c2, float h2) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLch expected = new(l2, c2, h2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 27b60846f49d840eb0468f24ab4a394850637c8d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 21:20:51 +1000 Subject: [PATCH 112/220] LAB YCBCR tests --- .../CieLabAndYCbCrConversionTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs new file mode 100644 index 000000000..6f4d7ec42 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(87.4179, 133.9763, 247.5308, 55.06287, 82.54838, 23.1697)] + public void Convert_YCbCr_to_CieLab(float y, float cb, float cr, float l, float a, float b) + { + // Arrange + YCbCr input = new(y, cb, cr); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(55.06287, 82.54838, 23.1697, 87.41701, 133.97232, 247.5314)] + public void Convert_CieLab_to_YCbCr(float l, float a, float b, float y, float cb, float cr) + { + // Arrange + CieLab input = new(l, a, b); + YCbCr expected = new(y, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 05b6cfb335bc4cd35dfeb29871c76d966ff5635e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 22:53:08 +1000 Subject: [PATCH 113/220] Create Hsl.cs --- src/ImageSharp/ColorProfiles/Hsl.cs | 243 ++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/Hsl.cs diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs new file mode 100644 index 000000000..dd8772d96 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -0,0 +1,243 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a Hsl (hue, saturation, lightness) color. +/// +public readonly struct Hsl : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(360, 1, 1); + + /// + /// Initializes a new instance of the struct. + /// + /// The h hue component. + /// The s saturation component. + /// The l value (lightness) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Hsl(float h, float s, float l) + : this(new Vector3(h, s, l)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the h, s, l components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Hsl(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.L = vector.Z; + } + + /// + /// Gets the hue component. + /// A value ranging between 0 and 360. + /// + public readonly float H { get; } + + /// + /// Gets the saturation component. + /// A value ranging between 0 and 1. + /// + public readonly float S { get; } + + /// + /// Gets the lightness component. + /// A value ranging between 0 and 1. + /// + public readonly float L { get; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Hsl left, Hsl right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right); + + /// + public static Hsl FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + float r = source.R; + float g = source.G; + float b = source.B; + + float max = MathF.Max(r, MathF.Max(g, b)); + float min = MathF.Min(r, MathF.Min(g, b)); + float chroma = max - min; + float h = 0F; + float s = 0F; + float l = (max + min) / 2F; + + if (MathF.Abs(chroma) < Constants.Epsilon) + { + return new Hsl(0F, s, l); + } + + if (MathF.Abs(r - max) < Constants.Epsilon) + { + h = (g - b) / chroma; + } + else if (MathF.Abs(g - max) < Constants.Epsilon) + { + h = 2F + ((b - r) / chroma); + } + else if (MathF.Abs(b - max) < Constants.Epsilon) + { + h = 4F + ((r - g) / chroma); + } + + h *= 60F; + if (h < 0F) + { + h += 360F; + } + + if (l <= .5F) + { + s = chroma / (max + min); + } + else + { + s = chroma / (2F - max - min); + } + + return new Hsl(h, s, l); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float rangedH = this.H / 360F; + float r = 0; + float g = 0; + float b = 0; + float s = this.S; + float l = this.L; + + if (MathF.Abs(l) > Constants.Epsilon) + { + if (MathF.Abs(s) < Constants.Epsilon) + { + r = g = b = l; + } + else + { + float temp2 = (l < .5F) ? l * (1F + s) : l + s - (l * s); + float temp1 = (2F * l) - temp2; + + r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F); + g = GetColorComponent(temp1, temp2, rangedH); + b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F); + } + } + + return new Rgb(r, g, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Hsl hsl = source[i]; + destination[i] = hsl.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L); + + /// + public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Hsl other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Hsl other) + => this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.L.Equals(other.L); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float GetColorComponent(float first, float second, float third) + { + third = MoveIntoRange(third); + if (third < 0.1666667F) + { + return first + ((second - first) * 6F * third); + } + + if (third < .5F) + { + return second; + } + + if (third < 0.6666667F) + { + return first + ((second - first) * (0.6666667F - third) * 6F); + } + + return first; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float MoveIntoRange(float value) + { + if (value < 0F) + { + value++; + } + else if (value > 1F) + { + value--; + } + + return value; + } +} From bb6502ca6a6bcde37d12bd5defee2cddf21f8055 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 22:57:00 +1000 Subject: [PATCH 114/220] Create Hsv.cs --- src/ImageSharp/ColorProfiles/Hsv.cs | 229 ++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/Hsv.cs diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs new file mode 100644 index 000000000..6a7e51332 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -0,0 +1,229 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). +/// +public readonly struct Hsv : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(360, 1, 1); + + /// + /// Initializes a new instance of the struct. + /// + /// The h hue component. + /// The s saturation component. + /// The v value (brightness) component. + [MethodImpl(InliningOptions.ShortMethod)] + public Hsv(float h, float s, float v) + : this(new Vector3(h, s, v)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the h, s, v components. + [MethodImpl(InliningOptions.ShortMethod)] + public Hsv(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.V = vector.Z; + } + + /// + /// Gets the hue component. + /// A value ranging between 0 and 360. + /// + public readonly float H { get; } + + /// + /// Gets the saturation component. + /// A value ranging between 0 and 1. + /// + public readonly float S { get; } + + /// + /// Gets the value (brightness) component. + /// A value ranging between 0 and 1. + /// + public readonly float V { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); + + /// + public static Hsv FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + float r = source.R; + float g = source.G; + float b = source.B; + + float max = MathF.Max(r, MathF.Max(g, b)); + float min = MathF.Min(r, MathF.Min(g, b)); + float chroma = max - min; + float h = 0; + float s = 0; + float v = max; + + if (MathF.Abs(chroma) < Constants.Epsilon) + { + return new Hsv(0, s, v); + } + + if (MathF.Abs(r - max) < Constants.Epsilon) + { + h = (g - b) / chroma; + } + else if (MathF.Abs(g - max) < Constants.Epsilon) + { + h = 2 + ((b - r) / chroma); + } + else if (MathF.Abs(b - max) < Constants.Epsilon) + { + h = 4 + ((r - g) / chroma); + } + + h *= 60; + if (h < 0.0) + { + h += 360; + } + + s = chroma / v; + + return new Hsv(h, s, v); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float s = this.S; + float v = this.V; + + if (MathF.Abs(s) < Constants.Epsilon) + { + return new Rgb(v, v, v); + } + + float h = (MathF.Abs(this.H - 360) < Constants.Epsilon) ? 0 : this.H / 60; + int i = (int)Math.Truncate(h); + float f = h - i; + + float p = v * (1F - s); + float q = v * (1F - (s * f)); + float t = v * (1F - (s * (1F - f))); + + float r, g, b; + switch (i) + { + case 0: + r = v; + g = t; + b = p; + break; + + case 1: + r = q; + g = v; + b = p; + break; + + case 2: + r = p; + g = v; + b = t; + break; + + case 3: + r = p; + g = q; + b = v; + break; + + case 4: + r = t; + g = p; + b = v; + break; + + default: + r = v; + g = p; + b = q; + break; + } + + return new Rgb(r, g, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Hsv hsv = source[i]; + destination[i] = hsv.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); + + /// + public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Hsv other) + => this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.V.Equals(other.V); +} From 698d76e8360b3fe821ef734ddb2f1fbc03dba1c1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 23:07:12 +1000 Subject: [PATCH 115/220] Create HunterLab.cs --- src/ImageSharp/ColorProfiles/HunterLab.cs | 202 ++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/HunterLab.cs diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs new file mode 100644 index 000000000..cf6dbd363 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -0,0 +1,202 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an Hunter LAB color. +/// . +/// +public readonly struct HunterLab : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HunterLab(float l, float a, float b) + : this(new Vector3(l, a, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l a b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HunterLab(Vector3 vector) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A { get; } + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B { get; } + + /// + /// Gets the reference white point of this color. + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right); + + /// + public static HunterLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Conversion algorithm described here: + // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + CieXyz whitePoint = options.TargetWhitePoint; + float x = source.X, y = source.Y, z = source.Z; + float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; + + float ka = ComputeKa(in whitePoint); + float kb = ComputeKb(in whitePoint); + + float yByYn = y / yn; + float sqrtYbyYn = MathF.Sqrt(yByYn); + float l = 100 * sqrtYbyYn; + float a = ka * (((x / xn) - yByYn) / sqrtYbyYn); + float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn); + + if (float.IsNaN(a)) + { + a = 0; + } + + if (float.IsNaN(b)) + { + b = 0; + } + + return new HunterLab(l, a, b); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyz xyz = source[i]; + destination[i] = FromProfileConnectingSpace(options, in xyz); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + CieXyz whitePoint = options.WhitePoint; + float l = this.L, a = this.A, b = this.B; + float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; + + float ka = ComputeKa(whitePoint); + float kb = ComputeKb(whitePoint); + + float pow = Numerics.Pow2(l / 100F); + float sqrtPow = MathF.Sqrt(pow); + float y = pow * yn; + + float x = (((a / ka) * sqrtPow) + pow) * xn; + float z = (((b / kb) * sqrtPow) - pow) * (-zn); + + return new CieXyz(x, y, z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + HunterLab lab = source[i]; + destination[i] = lab.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); + + /// + public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is HunterLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(HunterLab other) + => this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B) + && this.WhitePoint.Equals(other.WhitePoint); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ComputeKa(in CieXyz whitePoint) + { + if (whitePoint.Equals(Illuminants.C)) + { + return 175F; + } + + return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ComputeKb(in CieXyz whitePoint) + { + if (whitePoint == Illuminants.C) + { + return 70F; + } + + return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z); + } +} From e7a2445dba6a89d97dcc8e0291853ee792da4259 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 23:07:19 +1000 Subject: [PATCH 116/220] Cleanup --- src/ImageSharp/ColorProfiles/CieLab.cs | 6 ++++-- src/ImageSharp/ColorProfiles/Hsv.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 3578e2713..5cda362d4 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -104,7 +104,8 @@ public readonly struct CieLab : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html + // Conversion algorithm described here: + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html CieXyz whitePoint = options.TargetWhitePoint; float wx = whitePoint.X, wy = whitePoint.Y, wz = whitePoint.Z; @@ -174,5 +175,6 @@ public readonly struct CieLab : IProfileConnectingSpace } /// - public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 6a7e51332..cbcdd4a1b 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -20,7 +20,7 @@ public readonly struct Hsv : IColorProfile /// The h hue component. /// The s saturation component. /// The v value (brightness) component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(float h, float s, float v) : this(new Vector3(h, s, v)) { @@ -30,7 +30,7 @@ public readonly struct Hsv : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the h, s, v components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); @@ -65,7 +65,7 @@ public readonly struct Hsv : IColorProfile /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); /// @@ -76,7 +76,7 @@ public readonly struct Hsv : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); /// @@ -211,7 +211,7 @@ public readonly struct Hsv : IColorProfile => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); /// @@ -221,7 +221,7 @@ public readonly struct Hsv : IColorProfile public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) => this.H.Equals(other.H) && this.S.Equals(other.S) From 2fbabe828cf0d1ea67b5071312c05a4e0ec27ac4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 18 Apr 2024 21:48:41 +1000 Subject: [PATCH 117/220] Complete HunterLab and add more tests --- src/ImageSharp/ColorProfiles/HunterLab.cs | 4 +- .../ApproximateColorProfileComparer.cs | 17 ++++- .../CieLabAndHslConversionTests.cs | 70 +++++++++++++++++++ .../CieLabAndHsvConversionTests.cs | 70 +++++++++++++++++++ .../CieLabAndHunterLabConversionTests.cs | 70 +++++++++++++++++++ 5 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index cf6dbd363..97a9004f7 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -132,8 +132,8 @@ public readonly struct HunterLab : IColorProfile float l = this.L, a = this.A, b = this.B; float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; - float ka = ComputeKa(whitePoint); - float kb = ComputeKb(whitePoint); + float ka = ComputeKa(in whitePoint); + float kb = ComputeKb(in whitePoint); float pow = Numerics.Pow2(l / 100F); float sqrtPow = MathF.Sqrt(pow); diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index f67f47222..56d495a87 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -19,7 +19,10 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -49,6 +52,12 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Cmyk x, Cmyk y) => this.Equals(x.C, y.C) && this.Equals(x.M, y.M) && this.Equals(x.Y, y.Y) && this.Equals(x.K, y.K); + public bool Equals(Hsl x, Hsl y) => this.Equals(x.H, y.H) && this.Equals(x.S, y.S) && this.Equals(x.L, y.L); + + public bool Equals(Hsv x, Hsv y) => this.Equals(x.H, y.H) && this.Equals(x.S, y.S) && this.Equals(x.V, y.V); + + public bool Equals(HunterLab x, HunterLab y) => this.Equals(x.L, y.L) && this.Equals(x.A, y.A) && this.Equals(x.B, y.B); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -69,6 +78,12 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] Cmyk obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Hsl obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] Hsv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] HunterLab obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs new file mode 100644 index 000000000..96779f189 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.5, 55.063, 82.54868, 23.16508)] + public void Convert_Hsl_to_CieLab(float h, float s, float ll, float l, float a, float b) + { + // Arrange + Hsl input = new(h, s, ll); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54868, 23.16508, 336.9393, 1, 0.5)] + public void Convert_CieLab_to_Hsl(float l, float a, float b, float h, float s, float ll) + { + // Arrange + CieLab input = new(l, a, b); + Hsl expected = new(h, s, ll); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs new file mode 100644 index 000000000..3389da981 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.9999999, 55.063, 82.54871, 23.16504)] + public void Convert_Hsv_to_CieLab(float h, float s, float v, float l, float a, float b) + { + // Arrange + Hsv input = new(h, s, v); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54871, 23.16504, 336.9393, 1, 0.9999999)] + public void Convert_CieLab_to_Hsv(float l, float a, float b, float h, float s, float v) + { + // Arrange + CieLab input = new(l, a, b); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs new file mode 100644 index 000000000..1054d537a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(27.51646, 556.9392, -0.03974226, 33.074177, 281.48329, -0.06948)] + public void Convert_HunterLab_to_CieLab(float l2, float a2, float b2, float l, float a, float b) + { + // Arrange + HunterLab input = new(l2, a2, b2); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(33.074177, 281.48329, -0.06948, 27.51646, 556.9392, -0.03974226)] + public void Convert_CieLab_to_HunterLab(float l, float a, float b, float l2, float a2, float b2) + { + // Arrange + CieLab input = new(l, a, b); + HunterLab expected = new(l2, a2, b2); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 9c5a393650bc40928caa89765501fdaf7bea1484 Mon Sep 17 00:00:00 2001 From: Dan Kroymann Date: Mon, 22 Apr 2024 17:29:20 -0700 Subject: [PATCH 118/220] Fix another async-over-sync issue where DecodeAsync() would internally block on a synchronous stream read when detecting the image format --- src/ImageSharp/Image.Decode.cs | 89 +++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 887cb23ca..8eec3c55f 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -67,20 +68,70 @@ public abstract partial class Image int i; do { - i = stream.Read(headersBuffer, n, headerSize - n); + i = stream.Read(headersBuffer[n..headerSize]); n += i; } while (n < headerSize && i > 0); stream.Position = startPosition; + return InternalDetectFormat(configuration, headersBuffer[..n]); + } + + /// + /// By reading the header on the provided stream this calculates the images format. + /// + /// The general configuration. + /// The image stream to read the header from. + /// The token to monitor for cancellation requests. + /// The mime type or null if none found. + /// The input format is not recognized. + private static async ValueTask InternalDetectFormatAsync( + Configuration configuration, + Stream stream, + CancellationToken cancellationToken) + { + // We take a minimum of the stream length vs the max header size and always check below + // to ensure that only formats that headers fit within the given buffer length are tested. + int headerSize = (int)Math.Min(configuration.MaxHeaderSize, stream.Length); + if (headerSize <= 0) + { + ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager); + } + + using (IMemoryOwner memoryOwner = MemoryPool.Shared.Rent(headerSize)) + { + Memory headersBuffer = memoryOwner.Memory; + long startPosition = stream.Position; + + // Read doesn't always guarantee the full returned length so read a byte + // at a time until we get either our count or hit the end of the stream. + int n = 0; + int i; + do + { + i = await stream.ReadAsync(headersBuffer[n..headerSize], cancellationToken); + n += i; + } + while (n < headerSize && i > 0); + + stream.Position = startPosition; + + return InternalDetectFormat(configuration, headersBuffer.Span[..n]); + } + } + + private static IImageFormat InternalDetectFormat( + Configuration configuration, + ReadOnlySpan headersBuffer) + { // Does the given stream contain enough data to fit in the header for the format // and does that data match the format specification? // Individual formats should still check since they are public. IImageFormat? format = null; foreach (IImageFormatDetector formatDetector in configuration.ImageFormatsManager.FormatDetectors) { - if (formatDetector.HeaderSize <= headerSize && formatDetector.TryDetectFormat(headersBuffer, out IImageFormat? attemptFormat)) + if (formatDetector.HeaderSize <= headersBuffer.Length && formatDetector.TryDetectFormat(headersBuffer, out IImageFormat? attemptFormat)) { format = attemptFormat; } @@ -106,6 +157,22 @@ public abstract partial class Image return options.Configuration.ImageFormatsManager.GetDecoder(format); } + /// + /// By reading the header on the provided stream this calculates the images format. + /// + /// The general decoder options. + /// The image stream to read the header from. + /// The token to monitor for cancellation requests. + /// The . + private static async ValueTask DiscoverDecoderAsync( + DecoderOptions options, + Stream stream, + CancellationToken cancellationToken) + { + IImageFormat format = await InternalDetectFormatAsync(options.Configuration, stream, cancellationToken); + return options.Configuration.ImageFormatsManager.GetDecoder(format); + } + /// /// Decodes the image stream to the current image. /// @@ -122,14 +189,14 @@ public abstract partial class Image return decoder.Decode(options, stream); } - private static Task> DecodeAsync( + private static async Task> DecodeAsync( DecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - IImageDecoder decoder = DiscoverDecoder(options, stream); - return decoder.DecodeAsync(options, stream, cancellationToken); + IImageDecoder decoder = await DiscoverDecoderAsync(options, stream, cancellationToken); + return await decoder.DecodeAsync(options, stream, cancellationToken); } private static Image Decode(DecoderOptions options, Stream stream) @@ -138,13 +205,13 @@ public abstract partial class Image return decoder.Decode(options, stream); } - private static Task DecodeAsync( + private static async Task DecodeAsync( DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - IImageDecoder decoder = DiscoverDecoder(options, stream); - return decoder.DecodeAsync(options, stream, cancellationToken); + IImageDecoder decoder = await DiscoverDecoderAsync(options, stream, cancellationToken); + return await decoder.DecodeAsync(options, stream, cancellationToken); } /// @@ -166,12 +233,12 @@ public abstract partial class Image /// The stream. /// The token to monitor for cancellation requests. /// The . - private static Task InternalIdentifyAsync( + private static async Task InternalIdentifyAsync( DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - IImageDecoder decoder = DiscoverDecoder(options, stream); - return decoder.IdentifyAsync(options, stream, cancellationToken); + IImageDecoder decoder = await DiscoverDecoderAsync(options, stream, cancellationToken); + return await decoder.IdentifyAsync(options, stream, cancellationToken); } } From f37ac8e7044c015c19ecbfe99f9dd23a4a65a213 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Apr 2024 22:07:17 +1000 Subject: [PATCH 119/220] Add LMS and Lch tests --- src/ImageSharp/ColorProfiles/CieLch.cs | 9 +-- .../CieLabAndLmsConversionTests.cs | 70 ++++++++++++++++++ .../CieLchAndCieLuvConversionTests.cs | 71 +++++++++++++++++++ 3 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 23f250d0c..a8d70cfb7 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -12,12 +12,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// public readonly struct CieLch : IColorProfile { - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - private static readonly Vector3 Min = new(0, -200, 0); private static readonly Vector3 Max = new(100, 200, 360); @@ -165,5 +159,6 @@ public readonly struct CieLch : IColorProfile } /// - public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs new file mode 100644 index 000000000..77243268f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8303261, -0.5776886, 0.1133359, 30.66193, 291.57209, -11.25262)] + public void Convert_Lms_to_CieLab(float l2, float m, float s, float l, float a, float b) + { + // Arrange + Lms input = new(l2, m, s); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.66193, 291.57209, -11.25262, 0.8303261, -0.5776886, 0.1133359)] + public void Convert_CieLab_to_Lms(float l, float a, float b, float l2, float m, float s) + { + // Arrange + CieLab input = new(l, a, b); + Lms expected = new(l2, m, s); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs new file mode 100644 index 000000000..cb0e587be --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 34.89777, 187.6642, -7.181467)] + public void Convert_CieLch_to_CieLuv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + CieLch input = new(l, c, h); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(34.89777, 187.6642, -7.181467, 36.0555, 103.6901, 10.01514)] + public void Convert_CieLuv_to_CieLch(float l2, float u, float v, float l, float c, float h) + { + // Arrange + CieLuv input = new(l2, u, v); + CieLch expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 00162b640d59101cf759c7e93f734073a3607fa5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Apr 2024 22:34:29 +1000 Subject: [PATCH 120/220] Create CieLchAndCieXyyConversionTests.cs --- .../CieLchAndCieXyyConversionTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs new file mode 100644 index 000000000..94f5515bf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0003f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.67641, 0.22770, 0.09037)] + public void Convert_CieLch_to_CieXyy(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLch input = new(l, c, h); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.67641, 0.22770, 0.09037, 36.05544, 103.691315, 10.012783)] + public void Convert_CieXyy_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLch expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 22e1c064c2825d7d5bb81c5040f0effb2dc6f0ff Mon Sep 17 00:00:00 2001 From: Dan Kroymann Date: Wed, 24 Apr 2024 18:20:34 -0700 Subject: [PATCH 121/220] Use MemoryAllocator from configuration --- src/ImageSharp/Image.Decode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 8eec3c55f..7f58c6ecd 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -99,7 +99,7 @@ public abstract partial class Image ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager); } - using (IMemoryOwner memoryOwner = MemoryPool.Shared.Rent(headerSize)) + using (IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(headerSize)) { Memory headersBuffer = memoryOwner.Memory; long startPosition = stream.Position; From e0d1493c3b12788cb960c3c375d0e6ee1b0d39c0 Mon Sep 17 00:00:00 2001 From: Dan Kroymann Date: Wed, 24 Apr 2024 18:45:11 -0700 Subject: [PATCH 122/220] Call the new InternalDetectFormatAsync() from DetectFormatAsync() --- src/ImageSharp/Image.FromStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 63f9e64f6..21345fdeb 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -72,7 +72,7 @@ public abstract partial class Image => WithSeekableStreamAsync( options, stream, - (s, _) => Task.FromResult(InternalDetectFormat(options.Configuration, s)), + async (s, ct) => await InternalDetectFormatAsync(options.Configuration, s, ct).ConfigureAwait(false), cancellationToken); /// From 06312cbcfd98daec6b70b684a26e137440f82d7a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 25 Apr 2024 21:56:47 +0200 Subject: [PATCH 123/220] Update TestFormat.cs --- tests/ImageSharp.Tests/TestFormat.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 10211f386..1c7d31b50 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -229,8 +229,7 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat return this.testFormat.Sample(); } - protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); } public class TestDecoderOptions : ISpecializedDecoderOptions From eb1c871f79a0859fe68e34af391e73be68dc30ae Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 25 Apr 2024 22:21:36 +0200 Subject: [PATCH 124/220] Update build-and-test.yml --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d96bd90a9..a450aebf4 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -24,7 +24,7 @@ jobs: sdk: 8.0.x runtime: -x64 codecov: false - - os: macos-latest + - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable framework: net8.0 sdk: 8.0.x runtime: -x64 From aaeae24dd6b71ee2db7d465c336bbdca82988d5c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 25 Apr 2024 22:28:42 +0200 Subject: [PATCH 125/220] Undo dummy change --- tests/ImageSharp.Tests/TestFormat.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 1c7d31b50..10211f386 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -229,7 +229,8 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat return this.testFormat.Sample(); } - protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } public class TestDecoderOptions : ISpecializedDecoderOptions From ec14dc266f0ea121c4ee0624a1e8f4dda3d2961d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Apr 2024 21:54:00 +1000 Subject: [PATCH 126/220] Create CieLchuvAndCmykConversionTests.cs --- .../CieLchuvAndCmykConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs new file mode 100644 index 000000000..2fb97871e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchuvAndCmykConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 0.8576171, 0.7693201, 0.3440427, 36.0555, 103.6901, 10.01514)] + public void Convert_Cmyk_to_CieLchuv(float c2, float m, float y, float k, float l, float c, float h) + { + // Arrange + Cmyk input = new(c2, m, y, k); + CieLchuv expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 103.6901, 10.01514, 0, 0.8576171, 0.7693201, 0.3440427)] + public void Convert_CieLchuv_to_Cmyk(float l, float c, float h, float c2, float m, float y, float k) + { + // Arrange + CieLchuv input = new(l, c, h); + Cmyk expected = new(c2, m, y, k); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 4efb1211c0cc1d5ec84cb404e67c99ae32107e45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Apr 2024 22:01:58 +1000 Subject: [PATCH 127/220] Create CieLuvAndCieXyyConversionTests.cs --- .../CieLuvAndCieXyyConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs new file mode 100644 index 000000000..edc342eaa --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.5646762, 0.2932749, 0.09037033)] + public void Convert_CieLuv_to_CieXyy(float l, float u, float v, float x, float y, float yl) + { + // Arrange + CieLuv input = new(l, u, v); + CieXyy expected = new(x, y, yl); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5646762, 0.2932749, 0.09037033, 36.0555, 103.6901, 10.01514)] + public void Convert_CieXyy_to_CieLuv(float x, float y, float yl, float l, float u, float v) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From f3f1d5ac435ce44676d1eef7503900aff326f990 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:14:37 +1000 Subject: [PATCH 128/220] Create CieLuvAndHslConversionTests.cs --- .../CieLuvAndHslConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs new file mode 100644 index 000000000..c6fb40149 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.7115612, 0.3765343)] + public void Convert_CieLuv_to_Hsl(float l, float u, float v, float h, float s, float l2) + { + // Arrange + CieLuv input = new(l, u, v); + Hsl expected = new(h, s, l2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.7115612, 0.3765343, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsl_to_CieLuv(float h, float s, float l2, float l, float u, float v) + { + // Arrange + Hsl input = new(h, s, l2); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From cd6052eca91488a307bc09ee129818438fda7ef7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:18:34 +1000 Subject: [PATCH 129/220] Create CieLuvAndHsvConversionTests.cs --- .../CieLuvAndHsvConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs new file mode 100644 index 000000000..f736b9a28 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.8314762, 0.6444615)] + public void Convert_CieLuv_to_Hsv(float l, float u, float v, float h, float s, float v2) + { + // Arrange + CieLuv input = new(l, u, v); + Hsv expected = new(h, s, v2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.8314762, 0.6444615, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsv_to_CieLuv(float h, float s, float v2, float l, float u, float v) + { + // Arrange + Hsv input = new(h, s, v2); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From b983cd3d885ab60eefedff9c5f7d598d4e4cabcc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:26:25 +1000 Subject: [PATCH 130/220] Create CieLuvAndHunterLabConversionTests.cs --- .../CieLuvAndHunterLabConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs new file mode 100644 index 000000000..6f674f53e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 30.59289, 48.55542, 9.80487)] + public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + CieLuv input = new(l, u, v); + HunterLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.59289, 48.55542, 9.80487, 36.0555, 93.6901, 10.01514)] + public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) + { + // Arrange + HunterLab input = new(l2, a, b); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 9d1cb16cbd3f9d2f46abe08e1e6f2497eaff5ef5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 22:14:19 +1000 Subject: [PATCH 131/220] Add CIeLuv tests --- .../CieLuvAndLmsConversionTests.cs | 72 +++++++++++++++++++ .../CieLuvAndRgbConversionTests.cs | 72 +++++++++++++++++++ .../CieLuvAndYCbCrConversionTests.cs | 72 +++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs new file mode 100644 index 000000000..f2f5ada62 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.164352, 0.03267485, 0.0483408)] + public void Convert_CieLuv_to_Lms(float l, float u, float v, float l2, float m, float s) + { + // Arrange + CieLuv input = new(l, u, v); + Lms expected = new(l2, m, s); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.164352, 0.03267485, 0.0483408, 36.0555, 93.69009, 10.01514)] + public void Convert_Lms_to_CieLuv(float l2, float m, float s, float l, float u, float v) + { + // Arrange + Lms input = new(l2, m, s); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs new file mode 100644 index 000000000..b0106fc57 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.6444615, 0.1086071, 0.2213444)] + public void Convert_CieLuv_to_Rgb(float l, float u, float v, float r, float g, float b) + { + // Arrange + CieLuv input = new(l, u, v); + Rgb expected = new(r, g, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.6444615, 0.1086071, 0.2213444, 36.0555, 93.69012, 10.01514)] + public void Convert_Rgb_to_CieLuv(float r, float g, float b, float l, float u, float v) + { + // Arrange + Rgb input = new(r, g, b); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs new file mode 100644 index 000000000..6fdb440d7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(36.0555, 93.6901, 10.01514, 71.8283, 119.3174, 193.9839)] + public void Convert_CieLuv_to_YCbCr(float l, float u, float v, float y, float cb, float cr) + { + // Arrange + CieLuv input = new(l, u, v); + YCbCr expected = new(y, cb, cr); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(71.8283, 119.3174, 193.9839, 36.00565, 93.44593, 10.2234)] + public void Convert_YCbCr_to_CieLuv(float y, float cb, float cr, float l, float u, float v) + { + // Arrange + YCbCr input = new(y, cb, cr); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From cc49c7a9d76ad2b72f00e2cd0043df7757b44d72 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 22:55:15 +1000 Subject: [PATCH 132/220] Add Xyy and HSV/HSL tests --- src/ImageSharp/ColorProfiles/Hsv.cs | 2 +- .../CieXyyAndHslConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndHsvConversionTests.cs | 70 +++++++++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index cbcdd4a1b..787b8ad41 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -112,7 +112,7 @@ public readonly struct Hsv : IColorProfile } h *= 60; - if (h < 0.0) + if (h < 0f) { h += 360; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs new file mode 100644 index 000000000..3e93206ce --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.2138507)] + public void Convert_CieXyy_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + CieXyy input = new(x, y, yl); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.2138507, 0.32114, 0.59787, 0.10976)] + public void Convert_Hsl_to_CieXyy(float h, float s, float l, float x, float y, float yl) + { + // Arrange + Hsl input = new(h, s, l); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs new file mode 100644 index 000000000..4e2bea427 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.42770)] + public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + CieXyy input = new(x, y, yl); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.42770, 0.32114, 0.59787, 0.10976)] + public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) + { + // Arrange + Hsv input = new(h, s, v); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 9b99301cd8758e3e4c18341733d119679b7c200e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 23:51:20 +1000 Subject: [PATCH 133/220] Update CieXyzAndCieLabConversionTest.cs --- .../ColorProfiles/CieXyzAndCieLabConversionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs index da2bcbc87..a88798881 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -24,7 +24,7 @@ public class CieXyzAndCieLabConversionTest [InlineData(0, 0, -172.4138, 0, 0, 1.08883)] [InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] [InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] - [InlineData(10, -400, 20, 0, 0.011260, 0)] + [InlineData(10, -400, 20, -0.08712, 0.01126, -0.00192)] public void Convert_Lab_to_Xyz(float l, float a, float b, float x, float y, float z) { // Arrange From 3ac5f703f3c56ee365380e54de52bee121906413 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 12:24:03 +1000 Subject: [PATCH 134/220] Complete CieXyy tests --- .../CieXyyAndHunterLabConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndLmsConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndRgbConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndYCbCrConversionTests.cs | 70 +++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs new file mode 100644 index 000000000..a3b7d4b0f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 31.6467056, -33.00599, 25.67032)] + public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + HunterLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(31.6467056, -33.00599, 25.67032, 0.360555, 0.936901, 0.1001514)] + public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + HunterLab input = new(l, a, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs new file mode 100644 index 000000000..6974a864f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieXyyAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0.06631134, 0.1415282, -0.03809926)] + public void Convert_CieXyy_to_Lms(float x, float y, float yl, float l, float m, float s) + { + // Arrange + CieXyy input = new(x, y, yl); + Lms expected = new(l, m, s); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.06631134, 0.1415282, -0.03809926, 0.360555, 0.936901, 0.1001514)] + public void Convert_Lms_to_CieXyy(float l, float m, float s, float x, float y, float yl) + { + // Arrange + Lms input = new(l, m, s); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs new file mode 100644 index 000000000..18df2ce14 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.4277014, 0)] + public void Convert_CieXyy_to_Rgb(float x, float y, float yl, float r, float g, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0.4277014, 0, 0.32114, 0.59787, 0.10976)] + public void Convert_Rgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) + { + // Arrange + Rgb input = new(r, g, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs new file mode 100644 index 000000000..1fe359603 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 64.0204849, 91.87107, 82.33627)] + public void Convert_CieXyy_to_YCbCr(float x, float y, float yl, float y2, float cb, float cr) + { + // Arrange + CieXyy input = new(x, y, yl); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(64.0204849, 91.87107, 82.33627, 0.32114, 0.59787, 0.10976)] + public void Convert_YCbCr_to_CieXyy(float y2, float cb, float cr, float x, float y, float yl) + { + // Arrange + YCbCr input = new(y2, cb, cr); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From ba066f96b503e8f83196cdbb7a1fd2995076e0a6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 14:30:06 +1000 Subject: [PATCH 135/220] Fix namespace add first LCH test --- .../CieLabAndCieLchuvConversionTests.cs | 2 +- .../CieLabAndCieLuvConversionTests.cs | 2 +- .../CieLabAndCieXyyConversionTests.cs | 2 +- .../CieLabAndCmykConversionTests.cs | 2 +- .../CieLabAndRgbConversionTests.cs | 2 +- .../CieLabAndYCbCrConversionTests.cs | 2 +- .../CieLchuvAndCieLchConversionTests.cs | 2 +- .../CieLchuvAndCieLuvConversionTests.cs | 2 +- .../CieXyyAndLmsConversionTests.cs | 2 +- .../CieXyzAndCieLchConversionTests.cs | 70 +++++++++++++++++++ .../CieXyzAndCieXyyConversionTest.cs | 2 +- .../CmykAndYCbCrConversionTests.cs | 2 +- .../RgbAndCieXyzConversionTest.cs | 2 +- .../ColorProfiles/RgbAndCmykConversionTest.cs | 2 +- .../RgbAndYCbCrConversionTest.cs | 2 +- 15 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index 7bf7b4231..13016fe87 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index fb9f25bd8..cd1d08f5d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs index 074173da5..a464eeca1 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs index a13ad7d15..746671c4a 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs index 3b0b2f96a..0a0453bc6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs index 6f4d7ec42..9a29b1539 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs index 580e66e5e..5b94572f6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs index a65c7e81c..43fd62355 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs index 6974a864f..1a89cb26d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs new file mode 100644 index 000000000..bde3b8e17 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 161.235321, 143.157)] + public void Convert_CieXyz_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyz input = new(x, y, yl); + CieLch expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50697, 161.235321, 143.157, 0.3605551, 0.936901, 0.1001514)] + public void Convert_CieLch_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLch input = new(l, c, h); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs index c4fa554a6..7b1d0ac78 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs index ef3305ec7..df7490b96 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 5e867178a..783e38e7f 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -4,7 +4,7 @@ using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs index 3ae2996f2..4f4ecb70b 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs index 05c17e89e..91f7fc08e 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. From e67a67171c53eadbf76548475160f117ed3ed236 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 19:59:15 +1000 Subject: [PATCH 136/220] Add some XYZ tests --- src/ImageSharp/ColorProfiles/CieLuv.cs | 10 +-- .../CieXyzAndCieLchuvConversionTests.cs | 69 +++++++++++++++ .../CieXyzAndCieLuvConversionTest.cs | 86 +++++++++++++++++++ 3 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 8708ea255..304a14c7a 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -94,7 +94,7 @@ public readonly struct CieLuv : IColorProfile ? ((116 * Math.Pow(yr, e)) - 16d) : (CieConstants.Kappa * yr); - if (double.IsNaN(l)) + if (double.IsNaN(l) || l == -0d) { l = 0; } @@ -107,7 +107,7 @@ public readonly struct CieLuv : IColorProfile u = 0; } - if (double.IsNaN(v) || u == -0d) + if (double.IsNaN(v) || v == -0d) { v = 0; } @@ -151,17 +151,17 @@ public readonly struct CieLuv : IColorProfile double x = (d - b) / (a - c); double z = (x * a) + b; - if (double.IsNaN(x)) + if (double.IsNaN(x) || x == -0d) { x = 0; } - if (double.IsNaN(y)) + if (double.IsNaN(y) || y == -0d) { y = 0; } - if (double.IsNaN(z)) + if (double.IsNaN(z) || z == -0d) { z = 0; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs new file mode 100644 index 000000000..fa604250f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndCieLchuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 177.345169, 142.601547)] + public void Convert_CieXyz_to_CieLchuv(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyz input = new(x, y, yl); + CieLchuv expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50697, 177.345169, 142.601547, 0.360555, 0.936901, 0.1001514)] + public void Convert_CieLchuv_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLchuv input = new(l, c, h); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs new file mode 100644 index 000000000..68151eaa0 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieLuvConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 100, 50, 0, 0, 0)] + [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] + [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] + [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] + [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] + [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] + public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) + { + // Arrange + CieLuv input = new(l, u, v); + CieXyz expected = new(x, y, z); + + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] + [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] + [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] + [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] + [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] + public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) + { + // Arrange + CieXyz input = new(x, y, z); + CieLuv expected = new(l, u, v); + + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 467850f7570fd79f3c2e144fceb57c5542cd2307 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 8 May 2024 18:01:38 +0200 Subject: [PATCH 137/220] Fix overflow in MemoryAllocator.Create(options) (#2730) Fix an overlook from #2706. See https://github.com/SixLabors/ImageSharp/commit/92b82779ac8e7a032989533bb9a01ef092186b2e#r141770676. --- .../Memory/Allocators/MemoryAllocator.cs | 2 +- .../UniformUnmanagedPoolMemoryAllocatorTests.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs index b56bedd04..8eaf0b6d6 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -49,7 +49,7 @@ public abstract class MemoryAllocator UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(options.MaximumPoolSizeMegabytes); if (options.AllocationLimitMegabytes.HasValue) { - allocator.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024 * 1024; + allocator.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L; allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes); } diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index 077d0afed..aa34a5c10 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -442,4 +442,20 @@ public class UniformUnmanagedPoolMemoryAllocatorTests allocator.AllocateGroup(4 * oneMb, 1024).Dispose(); // Should work Assert.Throws(() => allocator.AllocateGroup(5 * oneMb, 1024)); } + + [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] + public void MemoryAllocator_Create_SetHighLimit() + { + RemoteExecutor.Invoke(RunTest).Dispose(); + static void RunTest() + { + const long threeGB = 3L * (1 << 30); + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + { + AllocationLimitMegabytes = (int)(threeGB / 1024) + }); + using MemoryGroup memoryGroup = allocator.AllocateGroup(threeGB, 1024); + Assert.Equal(threeGB, memoryGroup.TotalLength); + } + } } From 9fdf204086a399aa6991e62440e92d223a8d8078 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 10 May 2024 23:53:24 +1000 Subject: [PATCH 138/220] Complete conversion tests --- src/ImageSharp/ColorProfiles/CieLuv.cs | 10 +-- ...olorProfileConverterExtensionsCieLabRgb.cs | 57 +++++++++++++ ...olorProfileConverterExtensionsRgbCieLab.cs | 57 +++++++++++++ src/ImageSharp/ColorProfiles/Hsl.cs | 2 +- src/ImageSharp/ColorProfiles/Hsv.cs | 6 +- .../ColorProfiles/LmsAdaptationMatrix.cs | 2 +- .../CieXyyAndHsvConversionTests.cs | 4 +- .../CieXyyAndHunterLabConversionTests.cs | 4 +- .../CieXyzAndCieLuvConversionTest.cs | 49 +++++------ .../CieXyzAndHslConversionTests.cs | 70 ++++++++++++++++ .../CieXyzAndHsvConversionTests.cs | 70 ++++++++++++++++ .../CieXyzAndHunterLabConversionTest.cs | 74 +++++++++++++++++ .../CieXyzAndLmsConversionTest.cs | 4 +- .../CieXyzAndYCbCrConversionTests.cs | 70 ++++++++++++++++ .../CmykAndCieLchConversionTests.cs | 69 +++++++++++++++ .../CmykAndCieLuvConversionTests.cs | 70 ++++++++++++++++ .../CmykAndYCbCrConversionTests.cs | 4 +- .../RgbAndCieXyzConversionTest.cs | 8 +- .../ColorProfiles/RgbAndHslConversionTest.cs | 83 +++++++++++++++++++ .../ColorProfiles/RgbAndHsvConversionTest.cs | 81 ++++++++++++++++++ 20 files changed, 745 insertions(+), 49 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 304a14c7a..64541e496 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -46,11 +46,6 @@ public readonly struct CieLuv : IColorProfile /// public readonly float V { get; } - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - /// /// Compares two objects for equality. /// @@ -185,7 +180,7 @@ public readonly struct CieLuv : IColorProfile => ChromaticAdaptionWhitePointSource.WhitePoint; /// - public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); + public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V); /// public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); @@ -198,8 +193,7 @@ public readonly struct CieLuv : IColorProfile public bool Equals(CieLuv other) => this.L.Equals(other.L) && this.U.Equals(other.U) - && this.V.Equals(other.V) - && this.WhitePoint.Equals(other.WhitePoint); + && this.V.Equals(other.V); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs new file mode 100644 index 000000000..bbedb2e2b --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromAOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromA = pcsFromAOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromA); + + using IMemoryOwner pcsFromBOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromB = pcsFromBOwner.GetSpan(); + CieLab.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.FromProfileConnectionSpace(options, pcsFromB, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs new file mode 100644 index 000000000..baa92755c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromAOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromA = pcsFromAOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromA); + + using IMemoryOwner pcsFromBOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromB = pcsFromBOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieLab.FromProfileConnectionSpace(options, pcsFromB, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index dd8772d96..8dcef23c4 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -114,7 +114,7 @@ public readonly struct Hsl : IColorProfile } h *= 60F; - if (h < 0F) + if (h < -Constants.Epsilon) { h += 360F; } diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 787b8ad41..b3922d11f 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -111,10 +111,10 @@ public readonly struct Hsv : IColorProfile h = 4 + ((r - g) / chroma); } - h *= 60; - if (h < 0f) + h *= 60F; + if (h < -Constants.Epsilon) { - h += 360; + h += 360F; } s = chroma / v; diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs index 70bc5aef2..b5b2887cc 100644 --- a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// /// Matrix data obtained from: -/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization +/// Two New Von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization /// S. Bianco, R. Schettini /// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy /// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs index 4e2bea427..c2547ca84 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs @@ -15,7 +15,7 @@ public class CieXyyAndHsvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.42770)] - public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) + public void Convert_CieXyy_To_Hsv(float x, float y, float yl, float h, float s, float v) { // Arrange CieXyy input = new(x, y, yl); @@ -43,7 +43,7 @@ public class CieXyyAndHsvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(120, 1, 0.42770, 0.32114, 0.59787, 0.10976)] - public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) + public void Convert_Hsv_To_CieXyy(float h, float s, float v, float x, float y, float yl) { // Arrange Hsv input = new(h, s, v); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs index a3b7d4b0f..4f66538f0 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs @@ -15,7 +15,7 @@ public class CieXyyAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.360555, 0.936901, 0.1001514, 31.6467056, -33.00599, 25.67032)] - public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) + public void Convert_CieXyy_To_HunterLab(float x, float y, float yl, float l, float a, float b) { // Arrange CieXyy input = new(x, y, yl); @@ -43,7 +43,7 @@ public class CieXyyAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(31.6467056, -33.00599, 25.67032, 0.360555, 0.936901, 0.1001514)] - public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + public void Convert_HunterLab_To_CieXyy(float l, float a, float b, float x, float y, float yl) { // Arrange HunterLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs index 68151eaa0..4a6c3c4f8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -18,29 +18,28 @@ public class CieXyzAndCieLuvConversionTest [Theory] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 100, 50, 0, 0, 0)] - [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] - [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] - [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] - [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] - [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] - public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) + [InlineData(0.000493, 0.000111, 0, 0.10026589, 0.9332349, -0.00704865158)] + [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] + [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] + [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] + [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] + public void Convert_Xyz_To_Luv(float x, float y, float z, float l, float u, float v) { // Arrange - CieLuv input = new(l, u, v); - CieXyz expected = new(x, y, z); + CieXyz input = new(x, y, z); + CieLuv expected = new(l, u, v); ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; ColorProfileConverter converter = new(options); - Span inputSpan = new CieLuv[5]; + Span inputSpan = new CieXyz[5]; inputSpan.Fill(input); - Span actualSpan = new CieXyz[5]; + Span actualSpan = new CieLuv[5]; // Act - CieXyz actual = converter.Convert(input); - converter.Convert(inputSpan, actualSpan); + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); // Assert Assert.Equal(expected, actual, Comparer); @@ -53,27 +52,29 @@ public class CieXyzAndCieLuvConversionTest [Theory] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] - [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] - [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] - [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] - [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] - public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) + [InlineData(0, 100, 50, 0, 0, 0)] + [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] + [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] + [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] + [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] + [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] + public void Convert_Luv_To_Xyz(float l, float u, float v, float x, float y, float z) { // Arrange - CieXyz input = new(x, y, z); - CieLuv expected = new(l, u, v); + CieLuv input = new(l, u, v); + CieXyz expected = new(x, y, z); ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; ColorProfileConverter converter = new(options); - Span inputSpan = new CieXyz[5]; + Span inputSpan = new CieLuv[5]; inputSpan.Fill(input); - Span actualSpan = new CieLuv[5]; + Span actualSpan = new CieXyz[5]; // Act - CieLuv actual = converter.Convert(input); + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); // Assert Assert.Equal(expected, actual, Comparer); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs new file mode 100644 index 000000000..cffdb008b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)] + public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + CieXyz input = new(x, y, yl); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.5, 0.38506496, 0.716878653, 0.09710451)] + public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl) + { + // Arrange + Hsl input = new(h, s, l); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs new file mode 100644 index 000000000..d4a0022a4 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.9999999)] + public void Convert_CieXyz_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + CieXyz input = new(x, y, yl); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.9999999, 0.3850648, 0.7168785, 0.09710446)] + public void Convert_Hsv_to_CieXyz(float h, float s, float v, float x, float y, float yl) + { + // Arrange + Hsv input = new(h, s, v); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs new file mode 100644 index 000000000..aef26fe9a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndHunterLabConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 96.79365, -100.951096, 49.35507)] + public void Convert_Xyz_To_HunterLab(float x, float y, float z, float l, float a, float b) + { + // Arrange + CieXyz input = new(x, y, z); + HunterLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(31.6467056, -33.00599, 25.67032, 0.0385420471, 0.10015139, -0.0317969956)] + public void Convert_HunterLab_To_Xyz(float l, float a, float b, float x, float y, float z) + { + // Arrange + HunterLab input = new(l, a, b); + CieXyz expected = new(x, y, z); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs index 60938991e..185fcd256 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs @@ -22,7 +22,7 @@ public class CieXyzAndLmsConversionTest [InlineData(0.2664, 1.7135, -0.0685, 0, 1, 0)] [InlineData(-0.175737162, 0.039960061, 1.121059368, 0, 0, 1.08883)] [InlineData(0.2262677362, 0.0961411609, 0.0484570397, 0.216938, 0.150041, 0.048850)] - public void Convert_Lms_to_CieXyz(float l, float m, float s, float x, float y, float z) + public void Convert_Lms_To_CieXyz(float l, float m, float s, float x, float y, float z) { // Arrange Lms input = new(l, m, s); @@ -54,7 +54,7 @@ public class CieXyzAndLmsConversionTest [InlineData(0, 1, 0, 0.2664, 1.7135, -0.0685)] [InlineData(0, 0, 1.08883, -0.175737162, 0.039960061, 1.121059368)] [InlineData(0.216938, 0.150041, 0.048850, 0.2262677362, 0.0961411609, 0.0484570397)] - public void Convert_CieXyz_to_Lms(float x, float y, float z, float l, float m, float s) + public void Convert_CieXyz_To_Lms(float x, float y, float z, float l, float m, float s) { // Arrange CieXyz input = new(x, y, z); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs new file mode 100644 index 000000000..475673da8 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 149.685, 43.52769, 21.23457)] + public void Convert_CieXyz_to_YCbCr(float x, float y, float z, float y2, float cb, float cr) + { + // Arrange + CieXyz input = new(x, y, z); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(149.685, 43.52769, 21.23457, 0.38506496, 0.716878653, 0.0971045)] + public void Convert_YCbCr_to_CieXyz(float y2, float cb, float cr, float x, float y, float z) + { + // Arrange + YCbCr input = new(y2, cb, cr); + CieXyz expected = new(x, y, z); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs new file mode 100644 index 000000000..a5230eb31 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CmykAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85025, 64.77041, 118.2425)] + public void Convert_Cmyk_To_CieLch(float c, float m, float y, float k, float l, float c2, float h) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLch expected = new(l, c2, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(100, 3.81656E-05, 218.6598, 0, 1.192093E-07, 0, 5.960464E-08)] + [InlineData(62.85025, 64.77041, 118.2425, 0.286581, 0, 0.7975187, 0.34983)] + public void Convert_CieLch_To_Cmyk(float l, float c2, float h, float c, float m, float y, float k) + { + // Arrange + CieLch input = new(l, c2, h); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs new file mode 100644 index 000000000..cfbd08054 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CmykAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 100, -1.937151E-05, -3.874302E-05)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85024, -24.4844189, 54.8588524)] + public void Convert_Cmyk_To_CieLuv(float c, float m, float y, float k, float l, float u, float v) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLuv expected = new(l, u, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(100, -1.937151E-05, -3.874302E-05, 0, 5.96046448E-08, 0, 0)] + [InlineData(62.85024, -24.4844189, 54.8588524, 0.2865809, 0, 0.797518551, 0.3498301)] + public void Convert_CieLuv_To_Cmyk(float l, float u, float v, float c, float m, float y, float k) + { + // Arrange + CieLuv input = new(l, u, v); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs index df7490b96..64b47e2b9 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -15,7 +15,7 @@ public class CmykAndYCbCrConversionTests [Theory] [InlineData(0, 0, 0, 0, 255, 128, 128)] [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] - public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) + public void Convert_Cmyk_To_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) { // Arrange Cmyk input = new(c, m, y, k); @@ -43,7 +43,7 @@ public class CmykAndYCbCrConversionTests [Theory] [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] - public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) + public void Convert_YCbCr_To_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) { // Arrange YCbCr input = new(y2, cb, cr); diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 783e38e7f..f84892561 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -24,7 +24,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] - public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) + public void Convert_XYZ_D50_To_SRGB(float x, float y, float z, float r, float g, float b) { // Arrange CieXyz input = new(x, y, z); @@ -57,7 +57,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] - public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) + public void Convert_XYZ_D65_To_SRGB(float x, float y, float z, float r, float g, float b) { // Arrange CieXyz input = new(x, y, z); @@ -90,7 +90,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] - public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) + public void Convert_SRGB_To_XYZ_D50(float r, float g, float b, float x, float y, float z) { // Arrange Rgb input = new(r, g, b); @@ -123,7 +123,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] - public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) + public void Convert_SRGB_To_XYZ_D65(float r, float g, float b, float x, float y, float z) { // Arrange Rgb input = new(r, g, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs new file mode 100644 index 000000000..b63b9ca84 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +/// +public class RgbAndHslConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 1, 1, 1, 1, 1)] + [InlineData(360, 1, 1, 1, 1, 1)] + [InlineData(0, 1, .5F, 1, 0, 0)] + [InlineData(120, 1, .5F, 0, 1, 0)] + [InlineData(240, 1, .5F, 0, 0, 1)] + public void Convert_Hsl_To_Rgb(float h, float s, float l, float r, float g, float b) + { + // Arrange + Hsl input = new(h, s, l); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 0, 0, 1)] + [InlineData(1, 0, 0, 0, 1, .5F)] + [InlineData(0, 1, 0, 120, 1, .5F)] + [InlineData(0, 0, 1, 240, 1, .5F)] + [InlineData(0.7, 0.8, 0.6, 90, 0.3333, 0.7F)] + public void Convert_Rgb_To_Hsl(float r, float g, float b, float h, float s, float l) + { + // Arrange + Rgb input = new(r, g, b); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs new file mode 100644 index 000000000..b89b576b6 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class RgbAndHsvConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0, 1, 1, 1, 1)] + [InlineData(360, 1, 1, 1, 0, 0)] + [InlineData(0, 1, 1, 1, 0, 0)] + [InlineData(120, 1, 1, 0, 1, 0)] + [InlineData(240, 1, 1, 0, 0, 1)] + public void Convert_Hsv_To_Rgb(float h, float s, float v, float r, float g, float b) + { + // Arrange + Hsv input = new(h, s, v); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 0, 0, 1)] + [InlineData(1, 0, 0, 0, 1, 1)] + [InlineData(0, 1, 0, 120, 1, 1)] + [InlineData(0, 0, 1, 240, 1, 1)] + public void Convert_Rgb_To_Hsv(float r, float g, float b, float h, float s, float v) + { + // Arrange + Rgb input = new(r, g, b); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From a7edf1b41a5b17953bd1ece51a51be6247f276a3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 11 May 2024 00:04:39 +1000 Subject: [PATCH 139/220] Some renaming and docs --- .../ColorProfiles/ColorConversionOptions.cs | 7 +++--- ...cs => KnownChromaticAdaptationMatrices.cs} | 22 +++++++++++-------- ...kingSpaces.cs => KnownRgbWorkingSpaces.cs} | 4 ++-- .../RgbAndCieXyzConversionTest.cs | 8 +++---- 4 files changed, 23 insertions(+), 18 deletions(-) rename src/ImageSharp/ColorProfiles/{LmsAdaptationMatrix.cs => KnownChromaticAdaptationMatrices.cs} (77%) rename src/ImageSharp/ColorProfiles/{RgbWorkingSpaces.cs => KnownRgbWorkingSpaces.cs} (99%) diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 384ab6a7f..920fdd800 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -18,7 +18,7 @@ public class ColorConversionOptions /// /// Initializes a new instance of the class. /// - public ColorConversionOptions() => this.AdaptationMatrix = LmsAdaptationMatrix.Bradford; + public ColorConversionOptions() => this.AdaptationMatrix = KnownChromaticAdaptationMatrices.Bradford; /// /// Gets the memory allocator. @@ -38,15 +38,16 @@ public class ColorConversionOptions /// /// Gets the source working space used for companding in conversions from/to XYZ color space. /// - public RgbWorkingSpace RgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + public RgbWorkingSpace RgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb; /// /// Gets the destination working space used for companding in conversions from/to XYZ color space. /// - public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb; /// /// Gets the transformation matrix used in conversion to perform chromatic adaptation. + /// for further information. Default is Bradford. /// public Matrix4x4 AdaptationMatrix { diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs similarity index 77% rename from src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs rename to src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs index b5b2887cc..cbc76332b 100644 --- a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs @@ -6,19 +6,23 @@ using System.Numerics; namespace SixLabors.ImageSharp.ColorProfiles; /// -/// Matrices used for transformation from to , defining the cone response domain. +/// Provides matrices for chromatic adaptation, facilitating the adjustment of color values +/// under different light sources to maintain color constancy. This class supports common +/// adaptation transforms based on the von Kries coefficient law, which assumes independent +/// scaling of the cone responses in the human eye. These matrices can be applied to convert +/// color coordinates between different illuminants, ensuring consistent color appearance +/// across various lighting conditions. /// /// -/// Matrix data obtained from: -/// Two New Von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization -/// S. Bianco, R. Schettini -/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy -/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf +/// Supported adaptation matrices include the Bradford, von Kries, and Sharp transforms. +/// These matrices are typically used in conjunction with color space conversions, such as from XYZ +/// to RGB, to achieve accurate color rendition in digital imaging applications. /// -public static class LmsAdaptationMatrix + +public static class KnownChromaticAdaptationMatrices { /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) + /// von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) /// public static readonly Matrix4x4 VonKriesHPEAdjusted = Matrix4x4.Transpose(new Matrix4x4 @@ -36,7 +40,7 @@ public static class LmsAdaptationMatrix }); /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) + /// von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) /// public static readonly Matrix4x4 VonKriesHPE = Matrix4x4.Transpose(new Matrix4x4 diff --git a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs similarity index 99% rename from src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs rename to src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index c0d7f5074..3b15c7fe6 100644 --- a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.ColorProfiles; @@ -10,7 +10,7 @@ namespace SixLabors.ColorProfiles; /// /// Chromaticity coordinates based on: /// -public static class RgbWorkingSpaces +public static class KnownRgbWorkingSpaces { /// /// sRgb working space. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index f84892561..31f8a9478 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -28,7 +28,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -61,7 +61,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -94,7 +94,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -127,7 +127,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); From bf13f0095cf0b7f8261724e6b925a017f90e7957 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 12 May 2024 22:17:36 +1000 Subject: [PATCH 140/220] Naming and fixes --- src/ImageSharp/ColorProfiles/CieLab.cs | 2 +- .../ColorProfiles/ColorConversionOptions.cs | 4 +- .../ColorProfiles/ColorProfileConverter.cs | 15 ++ ...rProfileConverterExtensionsCieLabCieLab.cs | 8 +- ...rProfileConverterExtensionsCieLabCieXyz.cs | 6 +- ...olorProfileConverterExtensionsCieLabRgb.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 6 +- ...olorProfileConverterExtensionsCieXyzRgb.cs | 8 +- ...olorProfileConverterExtensionsRgbCieLab.cs | 6 +- ...olorProfileConverterExtensionsRgbCieXyz.cs | 8 +- .../ColorProfileConverterExtensionsRgbRgb.cs | 6 +- src/ImageSharp/ColorProfiles/HunterLab.cs | 4 +- .../{Illuminants.cs => KnownIlluminants.cs} | 4 +- .../ColorProfiles/KnownRgbWorkingSpaces.cs | 38 ++-- .../VonKriesChromaticAdaptation.cs | 55 ++--- .../CieLabAndCieLchConversionTests.cs | 4 +- .../CieLabAndCieLchuvConversionTests.cs | 4 +- .../CieLabAndCieLuvConversionTests.cs | 4 +- .../CieLchAndCieLuvConversionTests.cs | 4 +- .../CieLchuvAndCieLchConversionTests.cs | 4 +- .../CieLchuvAndCieLuvConversionTests.cs | 4 +- .../CieLchuvAndCmykConversionTests.cs | 4 +- .../CieLuvAndCieXyyConversionTests.cs | 4 +- .../CieLuvAndHslConversionTests.cs | 4 +- .../CieLuvAndHsvConversionTests.cs | 4 +- .../CieLuvAndHunterLabConversionTests.cs | 4 +- .../CieLuvAndLmsConversionTests.cs | 4 +- .../CieLuvAndRgbConversionTests.cs | 4 +- .../CieLuvAndYCbCrConversionTests.cs | 4 +- .../CieXyzAndCieLabConversionTest.cs | 4 +- .../CieXyzAndCieLuvConversionTest.cs | 4 +- ...ProfileConverterChomaticAdaptationTests.cs | 204 ++++++++++++++++++ .../RgbAndCieXyzConversionTest.cs | 8 +- .../VonKriesChromaticAdaptationTests.cs | 42 ++++ 35 files changed, 382 insertions(+), 118 deletions(-) rename src/ImageSharp/ColorProfiles/{Illuminants.cs => KnownIlluminants.cs} (96%) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 5cda362d4..d7dd7415d 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -16,7 +16,7 @@ public readonly struct CieLab : IProfileConnectingSpace /// D50 standard illuminant. /// Used when reference white is not specified explicitly. /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + public static readonly CieXyz DefaultWhitePoint = KnownIlluminants.D50; /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 920fdd800..93eb8bde1 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -28,12 +28,12 @@ public class ColorConversionOptions /// /// Gets the source white point used for chromatic adaptation in conversions from/to XYZ color space. /// - public CieXyz WhitePoint { get; init; } = Illuminants.D50; + public CieXyz WhitePoint { get; init; } = KnownIlluminants.D50; /// /// Gets the destination white point used for chromatic adaptation in conversions from/to XYZ color space. /// - public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + public CieXyz TargetWhitePoint { get; init; } = KnownIlluminants.D50; /// /// Gets the source working space used for companding in conversions from/to XYZ color space. diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs index af9ab0af8..18b90a622 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs @@ -27,4 +27,19 @@ public class ColorProfileConverter /// Gets the color profile conversion options. /// public ColorConversionOptions Options { get; } + + internal (CieXyz From, CieXyz To) GetChromaticAdaptionWhitePoints() + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? this.Options.WhitePoint + : this.Options.RgbWorkingSpace.WhitePoint; + + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? this.Options.TargetWhitePoint + : this.Options.TargetRgbWorkingSpace.WhitePoint; + + return (sourceWhitePoint, targetWhitePoint); + } } diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs index 51c68531d..9bfccb62b 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieLab.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 545b5dadf..4c86c8793 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs index bbedb2e2b..89e03f3ae 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsCieLabRgb CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabRgb CieLab.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index a73677129..de44cb17e 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +40,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index fd6ff21e4..cea7ea73a 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsFrom); @@ -36,7 +37,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsFrom, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs index b0ec427c5..0d267e56f 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +40,8 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs index baa92755c..50503bb07 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsRgbCieLab CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbCieLab Rgb.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs index f98d4bfc3..19d04e953 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -21,7 +21,8 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz Rgb.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs index 248a54026..f7edd4ba4 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsRgbRgb CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbRgb Rgb.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index 97a9004f7..5d6c01a2b 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -181,7 +181,7 @@ public readonly struct HunterLab : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) { - if (whitePoint.Equals(Illuminants.C)) + if (whitePoint.Equals(KnownIlluminants.C)) { return 175F; } @@ -192,7 +192,7 @@ public readonly struct HunterLab : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKb(in CieXyz whitePoint) { - if (whitePoint == Illuminants.C) + if (whitePoint == KnownIlluminants.C) { return 70F; } diff --git a/src/ImageSharp/ColorProfiles/Illuminants.cs b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs similarity index 96% rename from src/ImageSharp/ColorProfiles/Illuminants.cs rename to src/ImageSharp/ColorProfiles/KnownIlluminants.cs index 0295dc484..ac5cbea2c 100644 --- a/src/ImageSharp/ColorProfiles/Illuminants.cs +++ b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; ///
    /// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant /// -public static class Illuminants +public static class KnownIlluminants { /// /// Incandescent / Tungsten diff --git a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index 3b15c7fe6..dfb1ec631 100644 --- a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -19,96 +19,96 @@ public static class KnownRgbWorkingSpaces /// Uses proper companding function, according to: /// /// - public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Simplified sRgb working space (uses gamma companding instead of ). /// See also . /// - public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. /// - public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// - public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space. /// - public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space. /// - public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space. /// - public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space. /// - public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space. /// - public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space. /// - public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space. /// - public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, KnownIlluminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space. /// - public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space. /// - public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space. /// - public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space. /// - public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space. /// - public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space. /// - public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space. /// - public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space. /// - public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs index 4719750b7..2f9a52912 100644 --- a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -19,34 +19,24 @@ public static class VonKriesChromaticAdaptation /// /// Performs a linear transformation of a source color in to the destination color. /// - /// The type of color profile to convert from. - /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). - /// The color profile conversion options. /// The source color. + /// The conversion white points. + /// The chromatic adaptation matrix. /// The - public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) - where TFrom : struct, IColorProfile - where TTo : struct, IColorProfile + public static CieXyz Transform(in CieXyz source, (CieXyz From, CieXyz To) whitePoints, Matrix4x4 matrix) { - CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.WhitePoint - : options.RgbWorkingSpace.WhitePoint; + CieXyz from = whitePoints.From; + CieXyz to = whitePoints.To; - CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.TargetWhitePoint - : options.TargetRgbWorkingSpace.WhitePoint; - - if (sourceWhitePoint.Equals(targetWhitePoint)) + if (from.Equals(to)) { return new(source.X, source.Y, source.Z); } - Matrix4x4 matrix = options.AdaptationMatrix; - Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(from.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(to.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); @@ -58,41 +48,36 @@ public static class VonKriesChromaticAdaptation /// /// Performs a bulk linear transformation of a source color in to the destination color. /// - /// The type of color profile to convert from. - /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The color profile conversion options. /// The span to the source colors. /// The span to the destination colors. - public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) - where TFrom : struct, IColorProfile - where TTo : struct, IColorProfile + /// The conversion white points. + /// The chromatic adaptation matrix. + public static void Transform( + ReadOnlySpan source, + Span destination, + (CieXyz From, CieXyz To) whitePoints, + Matrix4x4 matrix) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); int count = source.Length; - CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.WhitePoint - : options.RgbWorkingSpace.WhitePoint; - - CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.TargetWhitePoint - : options.TargetRgbWorkingSpace.WhitePoint; + CieXyz from = whitePoints.From; + CieXyz to = whitePoints.To; - if (sourceWhitePoint.Equals(targetWhitePoint)) + if (from.Equals(to)) { source.CopyTo(destination[..count]); return; } - Matrix4x4 matrix = options.AdaptationMatrix; Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(from.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(to.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs index 0eaf30b81..9a894c776 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs @@ -30,7 +30,7 @@ public class CieLabAndCieLchConversionTests // Arrange CieLch input = new(l, c, h); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -65,7 +65,7 @@ public class CieLabAndCieLchConversionTests // Arrange CieLab input = new(l, a, b); CieLch expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index 13016fe87..4b1b5e1a5 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -24,7 +24,7 @@ public class CieLabAndCieLchuvConversionTests // Arrange CieLchuv input = new(l, c, h); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; @@ -53,7 +53,7 @@ public class CieLabAndCieLchuvConversionTests // Arrange CieLab input = new(l, a, b); CieLchuv expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index cd1d08f5d..44756c779 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -24,7 +24,7 @@ public class CieLabAndCieLuvConversionTests // Arrange CieLuv input = new(l, u, v); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -53,7 +53,7 @@ public class CieLabAndCieLuvConversionTests // Arrange CieLab input = new(l, a, b); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs index cb0e587be..598d4af33 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchAndCieLuvConversionTests // Arrange CieLch input = new(l, c, h); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -48,7 +48,7 @@ public class CieLchAndCieLuvConversionTests // Arrange CieLuv input = new(l2, u, v); CieLch expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs index 5b94572f6..a3e0b45e0 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchuvAndCieLchConversionTests // Arrange CieLch input = new(l2, c2, h2); CieLchuv expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -48,7 +48,7 @@ public class CieLchuvAndCieLchConversionTests // Arrange CieLchuv input = new(l, c, h); CieLch expected = new(l2, c2, h2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs index 43fd62355..465237490 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -30,7 +30,7 @@ public class CieLchuvAndCieLuvConversionTests // Arrange CieLchuv input = new(l, c, h); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; @@ -66,7 +66,7 @@ public class CieLchuvAndCieLuvConversionTests // Arrange CieLuv input = new(l, u, v); CieLchuv expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs index 2fb97871e..60ac3da16 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchuvAndCmykConversionTests // Arrange Cmyk input = new(c2, m, y, k); CieLchuv expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Cmyk[5]; @@ -49,7 +49,7 @@ public class CieLchuvAndCmykConversionTests // Arrange CieLchuv input = new(l, c, h); Cmyk expected = new(c2, m, y, k); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs index edc342eaa..e73edcda7 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndCieXyyConversionTests // Arrange CieLuv input = new(l, u, v); CieXyy expected = new(x, y, yl); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndCieXyyConversionTests // Arrange CieXyy input = new(x, y, yl); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieXyy[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs index c6fb40149..b178b22b2 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHslConversionTests // Arrange CieLuv input = new(l, u, v); Hsl expected = new(h, s, l2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHslConversionTests // Arrange Hsl input = new(h, s, l2); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Hsl[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs index f736b9a28..286609337 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHsvConversionTests // Arrange CieLuv input = new(l, u, v); Hsv expected = new(h, s, v2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHsvConversionTests // Arrange Hsv input = new(h, s, v2); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Hsv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs index 6f674f53e..ab44d2dca 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHunterLabConversionTests // Arrange CieLuv input = new(l, u, v); HunterLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHunterLabConversionTests // Arrange HunterLab input = new(l2, a, b); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new HunterLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs index f2f5ada62..812ca44dd 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndLmsConversionTests // Arrange CieLuv input = new(l, u, v); Lms expected = new(l2, m, s); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndLmsConversionTests // Arrange Lms input = new(l2, m, s); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Lms[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs index b0106fc57..f1da6e33f 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndRgbConversionTests // Arrange CieLuv input = new(l, u, v); Rgb expected = new(r, g, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndRgbConversionTests // Arrange Rgb input = new(r, g, b); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Rgb[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs index 6fdb440d7..fa7e2ece3 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndYCbCrConversionTests // Arrange CieLuv input = new(l, u, v); YCbCr expected = new(y, cb, cr); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndYCbCrConversionTests // Arrange YCbCr input = new(y, cb, cr); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new YCbCr[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs index a88798881..cb4d02889 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -29,7 +29,7 @@ public class CieXyzAndCieLabConversionTest { // Arrange CieLab input = new(l, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -62,7 +62,7 @@ public class CieXyzAndCieLabConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); CieLab expected = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs index 4a6c3c4f8..944b99005 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -29,7 +29,7 @@ public class CieXyzAndCieLuvConversionTest CieXyz input = new(x, y, z); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieXyz[5]; @@ -64,7 +64,7 @@ public class CieXyzAndCieLuvConversionTest CieLuv input = new(l, u, v); CieXyz expected = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs new file mode 100644 index 000000000..947f153c2 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs @@ -0,0 +1,204 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests chromatic adaptation within the . +/// Test data generated using: +/// +/// +/// +public class ColorProfileConverterChomaticAdaptationTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] + public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + Rgb input = new(r1, g1, b1); + Rgb expected = new(r2, g2, b2); + ColorConversionOptions options = new() + { + RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, + TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb + }; + ColorProfileConverter converter = new(options); + + // Action + Rgb actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] + public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + Rgb input = new(r1, g1, b1); + Rgb expected = new(r2, g2, b2); + ColorConversionOptions options = new() + { + RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb, + TargetRgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb + }; + ColorProfileConverter converter = new(options); + + // Action + Rgb actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] + public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + CieLab input = new(l1, a1, b1); + CieLab expected = new(l2, a2, b2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50 + }; + ColorProfileConverter converter = new(options); + + // Action + CieLab actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] + public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + CieXyz input = new(x1, y1, z1); + CieXyz expected = new(x2, y2, z2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.Bradford + }; + + ColorProfileConverter converter = new(options); + + // Action + CieXyz actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] + public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + CieXyz input = new(x1, y1, z1); + CieXyz expected = new(x2, y2, z2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieXyz actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.28086, 33.0681534, 1.30099022)] + public void Adapt_HunterLab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + HunterLab input = new(l1, a1, b1); + HunterLab expected = new(l2, a2, b2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + }; + + ColorProfileConverter converter = new(options); + + // Action + HunterLab actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22, 34.0843468, 359.009)] + public void Adapt_CieLchuv_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + CieLchuv input = new(l1, c1, h1); + CieLchuv expected = new(l2, c2, h2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieLchuv actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(22, 33, 1, 22, 33, 0.9999999)] + public void Adapt_CieLch_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + CieLch input = new(l1, c1, h1); + CieLch expected = new(l2, c2, h2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieLch actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 31f8a9478..960dc50fa 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -28,7 +28,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -61,7 +61,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -94,7 +94,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -127,7 +127,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); diff --git a/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs new file mode 100644 index 000000000..7f5687dee --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +public class VonKriesChromaticAdaptationTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + public static readonly TheoryData WhitePoints = new() + { + { KnownIlluminants.D65, KnownIlluminants.D50 }, + { KnownIlluminants.D65, KnownIlluminants.D65 } + }; + + [Theory] + [MemberData(nameof(WhitePoints))] + public void SingleAndBulkTransformYieldIdenticalResults(CieXyz from, CieXyz to) + { + ColorConversionOptions options = new() + { + WhitePoint = from, + TargetWhitePoint = to + }; + + CieXyz input = new(1, 0, 1); + CieXyz expected = VonKriesChromaticAdaptation.Transform(in input, (from, to), KnownChromaticAdaptationMatrices.Bradford); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + VonKriesChromaticAdaptation.Transform(inputSpan, actualSpan, (from, to), KnownChromaticAdaptationMatrices.Bradford); + + for (int i = 0; i < inputSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 72dbc1308c6b7eb94202b1969c9f5f3fa1ad6898 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 May 2024 23:12:19 +1000 Subject: [PATCH 141/220] Complete conversion tests --- src/ImageSharp/ColorProfiles/CieLab.cs | 6 +- src/ImageSharp/ColorProfiles/CieLuv.cs | 14 +++ src/ImageSharp/ColorProfiles/CieXyz.cs | 7 +- src/ImageSharp/ColorProfiles/Rgb.cs | 39 +++++-- .../CieLuvAndHunterLabConversionTests.cs | 4 +- .../ColorProfiles/CompandingTests.cs | 108 ++++++++++++++++++ .../ColorProfiles/RgbAndHslConversionTest.cs | 2 +- .../StringRepresentationTests.cs | 60 ++++++++++ 8 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index d7dd7415d..72148af45 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -26,8 +26,11 @@ public readonly struct CieLab : IProfileConnectingSpace /// The b (blue - yellow) component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(float l, float a, float b) - : this(new Vector3(l, a, b)) { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = l; + this.A = a; + this.B = b; } /// @@ -38,7 +41,6 @@ public readonly struct CieLab : IProfileConnectingSpace public CieLab(Vector3 vector) : this() { - // Not clamping as documentation about this space only indicates "usual" ranges this.L = vector.X; this.A = vector.Y; this.B = vector.Z; diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 64541e496..9bf102057 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -28,6 +29,19 @@ public readonly struct CieLuv : IColorProfile this.V = v; } + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, u, v components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLuv(Vector3 vector) + : this() + { + this.L = vector.X; + this.U = vector.Y; + this.V = vector.Z; + } + /// /// Gets the lightness dimension /// A value usually ranging between 0 and 100. diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index ec28a232d..fbd3e77d7 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.ColorProfiles; @@ -20,8 +21,11 @@ public readonly struct CieXyz : IProfileConnectingSpace /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz(float x, float y, float z) - : this(new Vector3(x, y, z)) { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = x; + this.Y = y; + this.Z = z; } /// @@ -31,7 +35,6 @@ public readonly struct CieXyz : IProfileConnectingSpace public CieXyz(Vector3 vector) : this() { - // Not clamping as documentation about this space only indicates "usual" ranges this.X = vector.X; this.Y = vector.Y; this.Z = vector.Z; diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 21575dd85..697c0fbd8 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -21,6 +21,7 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b) { + // Not clamping as this space can exceed "usual" ranges this.R = r; this.G = g; this.B = b; @@ -31,7 +32,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The vector representing the r, g, b components. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Rgb(Vector3 source) + public Rgb(Vector3 source) { this.R = source.X; this.G = source.Y; @@ -149,20 +150,32 @@ public readonly struct Rgb : IProfileConnectingSpace public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - /// Initializes the pixel instance from a generic scaled . + /// Initializes the color instance from a generic scaled . /// - /// The vector to load the pixel from. + /// The vector to load the color from. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb FromScaledVector3(Vector3 source) => new(source); + public static Rgb FromScaledVector3(Vector3 source) => new(Vector3.Clamp(source, Vector3.Zero, Vector3.One)); /// - /// Initializes the pixel instance from a generic scaled . + /// Initializes the color instance from a generic scaled . /// - /// The vector to load the pixel from. + /// The vector to load the color from. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z); + public static Rgb FromScaledVector4(Vector4 source) + { + source = Vector4.Clamp(source, Vector4.Zero, Vector4.One); + return new(source.X, source.Y, source.Z); + } + + /// + /// Initializes the color instance for a source clamped between 0 and 1 + /// + /// The source to load the color from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb Clamp(Rgb source) => new(Vector3.Clamp(new(source.R, source.G, source.B), Vector3.Zero, Vector3.One)); /// /// Expands the color into a generic ("scaled") representation @@ -171,7 +184,15 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector3 ToScaledVector3() => new(this.R, this.G, this.B); + public Vector3 ToScaledVector3() => Clamp(this).ToVector3(); + + /// + /// Expands the color into a generic representation. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.R, this.G, this.B); /// /// Expands the color into a generic ("scaled") representation @@ -180,7 +201,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => new(this.R, this.G, this.B, 1f); + public Vector4 ToScaledVector4() => new(this.ToScaledVector3(), 1f); private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) { diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs index ab44d2dca..73b605fb6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -15,7 +15,7 @@ public class CieLuvAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(36.0555, 93.6901, 10.01514, 30.59289, 48.55542, 9.80487)] - public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) + public void Convert_CieLuv_To_HunterLab(float l, float u, float v, float l2, float a, float b) { // Arrange CieLuv input = new(l, u, v); @@ -44,7 +44,7 @@ public class CieLuvAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(30.59289, 48.55542, 9.80487, 36.0555, 93.6901, 10.01514)] - public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) + public void Convert_HunterLab_To_CieLuv(float l2, float a, float b, float l, float u, float v) { // Arrange HunterLab input = new(l2, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs new file mode 100644 index 000000000..1bdefa109 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. +/// +public class CompandingTests +{ + private static readonly ApproximateFloatComparer Comparer = new(.000001F); + + [Fact] + public void Rec2020Companding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = Rec2020Companding.Expand(input); + Vector4 c = Rec2020Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .44847462F, input); + } + + [Fact] + public void Rec709Companding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = Rec709Companding.Expand(input); + Vector4 c = Rec709Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .4483577F, input); + } + + [Fact] + public void SRgbCompanding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = SRgbCompanding.Expand(input); + Vector4 c = SRgbCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .40242353F, input); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Expand_VectorSpan(int length) + { + Random rnd = new(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + expected[i] = SRgbCompanding.Expand(source[i]); + } + + SRgbCompanding.Expand(source); + + Assert.Equal(expected, source, Comparer); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Compress_VectorSpan(int length) + { + Random rnd = new(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + expected[i] = SRgbCompanding.Compress(source[i]); + } + + SRgbCompanding.Compress(source); + + Assert.Equal(expected, source, Comparer); + } + + [Fact] + public void GammaCompanding_IsCorrect() + { + const double gamma = 2.2; + Vector4 input = new(.667F); + Vector4 e = GammaCompanding.Expand(input, gamma); + Vector4 c = GammaCompanding.Compress(e, gamma); + CompandingIsCorrectImpl(e, c, .41027668F, input); + } + + [Fact] + public void LCompanding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = LCompanding.Expand(input); + Vector4 c = LCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .36236193F, input); + } + + private static void CompandingIsCorrectImpl(Vector4 e, Vector4 c, float expanded, Vector4 compressed) + { + // W (alpha) is already the linear representation of the color. + Assert.Equal(new Vector4(expanded, expanded, expanded, e.W), e, Comparer); + Assert.Equal(compressed, c, Comparer); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs index b63b9ca84..0dc95628b 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs new file mode 100644 index 000000000..770c987db --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +public class StringRepresentationTests +{ + private static readonly Vector3 One = new(1); + private static readonly Vector3 Zero = new(0); + private static readonly Vector3 Random = new(42.4F, 94.5F, 83.4F); + + public static readonly TheoryData TestData = new() + { + { new CieLab(Zero), "CieLab(0, 0, 0)" }, + { new CieLch(Zero), "CieLch(0, 0, 0)" }, + { new CieLchuv(Zero), "CieLchuv(0, 0, 0)" }, + { new CieLuv(Zero), "CieLuv(0, 0, 0)" }, + { new CieXyz(Zero), "CieXyz(0, 0, 0)" }, + { new CieXyy(Zero), "CieXyy(0, 0, 0)" }, + { new HunterLab(Zero), "HunterLab(0, 0, 0)" }, + { new Lms(Zero), "Lms(0, 0, 0)" }, + { new Rgb(Zero), "Rgb(0, 0, 0)" }, + { new Hsl(Zero), "Hsl(0, 0, 0)" }, + { new Hsv(Zero), "Hsv(0, 0, 0)" }, + { new YCbCr(Zero), "YCbCr(0, 0, 0)" }, + { new CieLab(One), "CieLab(1, 1, 1)" }, + { new CieLch(One), "CieLch(1, 1, 1)" }, + { new CieLchuv(One), "CieLchuv(1, 1, 1)" }, + { new CieLuv(One), "CieLuv(1, 1, 1)" }, + { new CieXyz(One), "CieXyz(1, 1, 1)" }, + { new CieXyy(One), "CieXyy(1, 1, 1)" }, + { new HunterLab(One), "HunterLab(1, 1, 1)" }, + { new Lms(One), "Lms(1, 1, 1)" }, + { new Rgb(One), "Rgb(1, 1, 1)" }, + { new Hsl(One), "Hsl(1, 1, 1)" }, + { new Hsv(One), "Hsv(1, 1, 1)" }, + { new YCbCr(One), "YCbCr(1, 1, 1)" }, + { new CieXyChromaticityCoordinates(1, 1), "CieXyChromaticityCoordinates(1, 1)" }, + { new CieLab(Random), "CieLab(42.4, 94.5, 83.4)" }, + { new CieLch(Random), "CieLch(42.4, 94.5, 83.4)" }, + { new CieLchuv(Random), "CieLchuv(42.4, 94.5, 83.4)" }, + { new CieLuv(Random), "CieLuv(42.4, 94.5, 83.4)" }, + { new CieXyz(Random), "CieXyz(42.4, 94.5, 83.4)" }, + { new CieXyy(Random), "CieXyy(42.4, 94.5, 83.4)" }, + { new HunterLab(Random), "HunterLab(42.4, 94.5, 83.4)" }, + { new Lms(Random), "Lms(42.4, 94.5, 83.4)" }, + { new Rgb(Random), "Rgb(42.4, 94.5, 83.4)" }, + { Rgb.Clamp(new Rgb(Random)), "Rgb(1, 1, 1)" }, + { new Hsl(Random), "Hsl(42.4, 1, 1)" }, // clamping to 1 is expected + { new Hsv(Random), "Hsv(42.4, 1, 1)" }, // clamping to 1 is expected + { new YCbCr(Random), "YCbCr(42.4, 94.5, 83.4)" }, + }; + + [Theory] + [MemberData(nameof(TestData))] + public void StringRepresentationsAreCorrect(object color, string text) => Assert.Equal(text, color.ToString()); +} From c16582b764005fa5570cd8e00603cb9af47ea566 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 09:38:47 +1000 Subject: [PATCH 142/220] Complete tests --- .../ColorProfiles/CieLabTests.cs | 44 ++++++++++ .../ColorProfiles/CieLchTests.cs | 42 ++++++++++ .../ColorProfiles/CieLchuvTests.cs | 42 ++++++++++ .../ColorProfiles/CieLuvTests.cs | 42 ++++++++++ .../CieXyChromaticityCoordinatesTests.cs | 41 ++++++++++ .../ColorProfiles/CieXyyTests.cs | 42 ++++++++++ .../ColorProfiles/CieXyzTests.cs | 42 ++++++++++ .../ColorProfiles/CmykTests.cs | 44 ++++++++++ .../ColorProfiles/HslTests.cs | 42 ++++++++++ .../ColorProfiles/HsvTests.cs | 42 ++++++++++ .../ColorProfiles/HunterLabTests.cs | 43 ++++++++++ .../ColorProfiles/LmsTests.cs | 43 ++++++++++ .../ColorProfiles/RgbTests.cs | 81 +++++++++++++++++++ .../ColorProfiles/YCbCrTests.cs | 42 ++++++++++ 14 files changed, 632 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HslTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs new file mode 100644 index 000000000..3c015259b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLabTests +{ + [Fact] + public void CieLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + CieLab cieLab = new(l, a, b); + + Assert.Equal(l, cieLab.L); + Assert.Equal(a, cieLab.A); + Assert.Equal(b, cieLab.B); + } + + [Fact] + public void CieLabEquality() + { + CieLab x = default; + CieLab y = new(Vector3.One); + + Assert.True(default == default(CieLab)); + Assert.True(new CieLab(1, 0, 1) != default); + Assert.False(new CieLab(1, 0, 1) == default); + Assert.Equal(default, default(CieLab)); + Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); + Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(new CieLab(1, 0, 1) == default); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs new file mode 100644 index 000000000..484db3e8c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLchTests +{ + [Fact] + public void CieLchConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + CieLch cieLch = new(l, c, h); + + Assert.Equal(l, cieLch.L); + Assert.Equal(c, cieLch.C); + Assert.Equal(h, cieLch.H); + } + + [Fact] + public void CieLchEquality() + { + CieLch x = default; + CieLch y = new(Vector3.One); + + Assert.True(default == default(CieLch)); + Assert.False(default != default(CieLch)); + Assert.Equal(default, default(CieLch)); + Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1)); + Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs new file mode 100644 index 000000000..0b737cdfc --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLchuvTests +{ + [Fact] + public void CieLchuvConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + CieLchuv cieLchuv = new(l, c, h); + + Assert.Equal(l, cieLchuv.L); + Assert.Equal(c, cieLchuv.C); + Assert.Equal(h, cieLchuv.H); + } + + [Fact] + public void CieLchuvEquality() + { + CieLchuv x = default; + CieLchuv y = new(Vector3.One); + + Assert.True(default == default(CieLchuv)); + Assert.False(default != default(CieLchuv)); + Assert.Equal(default, default(CieLchuv)); + Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1)); + Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs new file mode 100644 index 000000000..db903a0bf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLuvTests +{ + [Fact] + public void CieLuvConstructorAssignsFields() + { + const float l = 75F; + const float c = -64F; + const float h = 87F; + CieLuv cieLuv = new(l, c, h); + + Assert.Equal(l, cieLuv.L); + Assert.Equal(c, cieLuv.U); + Assert.Equal(h, cieLuv.V); + } + + [Fact] + public void CieLuvEquality() + { + CieLuv x = default; + CieLuv y = new(Vector3.One); + + Assert.True(default == default(CieLuv)); + Assert.False(default != default(CieLuv)); + Assert.Equal(default, default(CieLuv)); + Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1)); + Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs new file mode 100644 index 000000000..a85a08a21 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyChromaticityCoordinatesTests +{ + [Fact] + public void CieXyChromaticityCoordinatesConstructorAssignsFields() + { + const float x = .75F; + const float y = .64F; + CieXyChromaticityCoordinates coordinates = new(x, y); + + Assert.Equal(x, coordinates.X); + Assert.Equal(y, coordinates.Y); + } + + [Fact] + public void CieXyChromaticityCoordinatesEquality() + { + CieXyChromaticityCoordinates x = default; + CieXyChromaticityCoordinates y = new(1, 1); + + Assert.True(default == default(CieXyChromaticityCoordinates)); + Assert.True(new CieXyChromaticityCoordinates(1, 0) != default); + Assert.False(new CieXyChromaticityCoordinates(1, 0) == default); + Assert.Equal(default, default(CieXyChromaticityCoordinates)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1)); + Assert.False(x.Equals(y)); + Assert.False(new CieXyChromaticityCoordinates(1, 0) == default); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs new file mode 100644 index 000000000..245512f8a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyyTests +{ + [Fact] + public void CieXyyConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float yl = 287F; + CieXyy cieXyy = new(x, y, yl); + + Assert.Equal(x, cieXyy.X); + Assert.Equal(y, cieXyy.Y); + Assert.Equal(y, cieXyy.Y); + } + + [Fact] + public void CieXyyEquality() + { + CieXyy x = default; + CieXyy y = new(Vector3.One); + + Assert.True(default == default(CieXyy)); + Assert.False(default != default(CieXyy)); + Assert.Equal(default, default(CieXyy)); + Assert.Equal(new CieXyy(1, 0, 1), new CieXyy(1, 0, 1)); + Assert.Equal(new CieXyy(Vector3.One), new CieXyy(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs new file mode 100644 index 000000000..88138304a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyzTests +{ + [Fact] + public void CieXyzConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float z = 287F; + CieXyz cieXyz = new(x, y, z); + + Assert.Equal(x, cieXyz.X); + Assert.Equal(y, cieXyz.Y); + Assert.Equal(z, cieXyz.Z); + } + + [Fact] + public void CieXyzEquality() + { + CieXyz x = default; + CieXyz y = new(Vector3.One); + + Assert.True(default == default(CieXyz)); + Assert.False(default != default(CieXyz)); + Assert.Equal(default, default(CieXyz)); + Assert.Equal(new CieXyz(1, 0, 1), new CieXyz(1, 0, 1)); + Assert.Equal(new CieXyz(Vector3.One), new CieXyz(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs new file mode 100644 index 000000000..e2044a75d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CmykTests +{ + [Fact] + public void CmykConstructorAssignsFields() + { + const float c = .75F; + const float m = .64F; + const float y = .87F; + const float k = .334F; + Cmyk cmyk = new(c, m, y, k); + + Assert.Equal(c, cmyk.C); + Assert.Equal(m, cmyk.M); + Assert.Equal(y, cmyk.Y); + Assert.Equal(k, cmyk.K); + } + + [Fact] + public void CmykEquality() + { + Cmyk x = default; + Cmyk y = new(Vector4.One); + + Assert.True(default == default(Cmyk)); + Assert.False(default != default(Cmyk)); + Assert.Equal(default, default(Cmyk)); + Assert.Equal(new Cmyk(1, 0, 1, 0), new Cmyk(1, 0, 1, 0)); + Assert.Equal(new Cmyk(Vector4.One), new Cmyk(Vector4.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs new file mode 100644 index 000000000..61eb3db66 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HslTests +{ + [Fact] + public void HslConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float l = .87F; + Hsl hsl = new(h, s, l); + + Assert.Equal(h, hsl.H); + Assert.Equal(s, hsl.S); + Assert.Equal(l, hsl.L); + } + + [Fact] + public void HslEquality() + { + Hsl x = default; + Hsl y = new(Vector3.One); + + Assert.True(default == default(Hsl)); + Assert.False(default != default(Hsl)); + Assert.Equal(default, default(Hsl)); + Assert.Equal(new Hsl(1, 0, 1), new Hsl(1, 0, 1)); + Assert.Equal(new Hsl(Vector3.One), new Hsl(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs new file mode 100644 index 000000000..46f58b18e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HsvTests +{ + [Fact] + public void HsvConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float v = .87F; + Hsv hsv = new(h, s, v); + + Assert.Equal(h, hsv.H); + Assert.Equal(s, hsv.S); + Assert.Equal(v, hsv.V); + } + + [Fact] + public void HsvEquality() + { + Hsv x = default; + Hsv y = new(Vector3.One); + + Assert.True(default == default(Hsv)); + Assert.False(default != default(Hsv)); + Assert.Equal(default, default(Hsv)); + Assert.Equal(new Hsv(1, 0, 1), new Hsv(1, 0, 1)); + Assert.Equal(new Hsv(Vector3.One), new Hsv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs new file mode 100644 index 000000000..5fbdd0788 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HunterLabTests +{ + [Fact] + public void HunterLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + HunterLab hunterLab = new(l, a, b); + + Assert.Equal(l, hunterLab.L); + Assert.Equal(a, hunterLab.A); + Assert.Equal(b, hunterLab.B); + } + + [Fact] + public void HunterLabEquality() + { + HunterLab x = default; + HunterLab y = new(Vector3.One); + + Assert.True(default == default(HunterLab)); + Assert.True(new HunterLab(1, 0, 1) != default); + Assert.False(new HunterLab(1, 0, 1) == default); + Assert.Equal(default, default(HunterLab)); + Assert.Equal(new HunterLab(1, 0, 1), new HunterLab(1, 0, 1)); + Assert.Equal(new HunterLab(Vector3.One), new HunterLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs new file mode 100644 index 000000000..138fd544d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class LmsTests +{ + [Fact] + public void LmsConstructorAssignsFields() + { + const float l = 75F; + const float m = -64F; + const float s = 87F; + Lms lms = new(l, m, s); + + Assert.Equal(l, lms.L); + Assert.Equal(m, lms.M); + Assert.Equal(s, lms.S); + } + + [Fact] + public void LmsEquality() + { + Lms x = default; + Lms y = new(Vector3.One); + + Assert.True(default == default(Lms)); + Assert.True(new Lms(1, 0, 1) != default); + Assert.False(new Lms(1, 0, 1) == default); + Assert.Equal(default, default(Lms)); + Assert.Equal(new Lms(1, 0, 1), new Lms(1, 0, 1)); + Assert.Equal(new Lms(Vector3.One), new Lms(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs new file mode 100644 index 000000000..7e4d4ee0e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class RgbTests +{ + [Fact] + public void RgbConstructorAssignsFields() + { + const float r = .75F; + const float g = .64F; + const float b = .87F; + Rgb rgb = new(r, g, b); + + Assert.Equal(r, rgb.R); + Assert.Equal(g, rgb.G); + Assert.Equal(b, rgb.B); + } + + [Fact] + public void RgbEquality() + { + Rgb x = default; + Rgb y = new(Vector3.One); + + Assert.True(default == default(Rgb)); + Assert.False(default != default(Rgb)); + Assert.Equal(default, default(Rgb)); + Assert.Equal(new Rgb(1, 0, 1), new Rgb(1, 0, 1)); + Assert.Equal(new Rgb(Vector3.One), new Rgb(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + + [Fact] + public void RgbAndRgb24Interop() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgb24 rgb24 = Rgb24.FromScaledVector4(new Rgb(r / 255F, g / 255F, b / 255F).ToScaledVector4()); + Rgb rgb2 = Rgb.FromScaledVector4(rgb24.ToScaledVector4()); + + Assert.Equal(r, rgb24.R); + Assert.Equal(g, rgb24.G); + Assert.Equal(b, rgb24.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } + + [Fact] + public void RgbAndRgba32Interop() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgba32 rgba32 = Rgba32.FromScaledVector4(new Rgb(r / 255F, g / 255F, b / 255F).ToScaledVector4()); + Rgb rgb2 = Rgb.FromScaledVector4(rgba32.ToScaledVector4()); + + Assert.Equal(r, rgba32.R); + Assert.Equal(g, rgba32.G); + Assert.Equal(b, rgba32.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs new file mode 100644 index 000000000..f8404ad94 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class YCbCrTests +{ + [Fact] + public void YCbCrConstructorAssignsFields() + { + const float y = 75F; + const float cb = 64F; + const float cr = 87F; + YCbCr yCbCr = new(y, cb, cr); + + Assert.Equal(y, yCbCr.Y); + Assert.Equal(cb, yCbCr.Cb); + Assert.Equal(cr, yCbCr.Cr); + } + + [Fact] + public void YCbCrEquality() + { + YCbCr x = default; + YCbCr y = new(Vector3.One); + + Assert.True(default == default(YCbCr)); + Assert.False(default != default(YCbCr)); + Assert.Equal(default, default(YCbCr)); + Assert.Equal(new YCbCr(1, 0, 1), new YCbCr(1, 0, 1)); + Assert.Equal(new YCbCr(Vector3.One), new YCbCr(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} From 355bd04b8ddb1734aab7f28c10ef7716979a4b77 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:02:42 +1000 Subject: [PATCH 143/220] Update benchmarks --- .../ColorProfiles/ColorConversionOptions.cs | 1 - .../KnownChromaticAdaptationMatrices.cs | 1 - .../ColorProfiles/KnownRgbWorkingSpaces.cs | 5 ++-- .../Color/ColorEquality.cs | 14 ++++------- .../Color/ColorspaceCieXyzToCieLabConvert.cs | 23 ++++++------------- .../ColorspaceCieXyzToHunterLabConvert.cs | 23 ++++++------------- .../Color/ColorspaceCieXyzToLmsConvert.cs | 23 ++++++------------- .../Color/ColorspaceCieXyzToRgbConvert.cs | 23 ++++++------------- .../Color/RgbWorkingSpaceAdapt.cs | 23 ++++++------------- .../ImageSharp.Benchmarks/Color/YcbCrToRgb.cs | 12 +++++----- 10 files changed, 47 insertions(+), 101 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 93eb8bde1..1eb118834 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs index cbc76332b..71d565f87 100644 --- a/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs +++ b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// These matrices are typically used in conjunction with color space conversions, such as from XYZ /// to RGB, to achieve accurate color rendition in digital imaging applications. /// - public static class KnownChromaticAdaptationMatrices { /// diff --git a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index dfb1ec631..916383936 100644 --- a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; -namespace SixLabors.ColorProfiles; +namespace SixLabors.ImageSharp.ColorProfiles; /// /// Chromaticity coordinates based on: diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 1ff8013b2..5166c89a9 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -1,25 +1,19 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.PixelFormats; +using SystemColor = System.Drawing.Color; namespace SixLabors.ImageSharp.Benchmarks; -using SystemColor = System.Drawing.Color; - public class ColorEquality { [Benchmark(Baseline = true, Description = "System.Drawing Color Equals")] public bool SystemDrawingColorEqual() - { - return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); - } + => SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); [Benchmark(Description = "ImageSharp Color Equals")] public bool ColorEqual() - { - return new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); - } + => new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index da09a8523..1f8a6b193 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -2,34 +2,25 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToCieLabConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.D50).ToLab(Illuminants.D50).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToCieLab(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index c3317c5d9..b11e78819 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -2,34 +2,25 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToHunterLabConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.C).ToHunterLab(Illuminants.C).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToHunterLab(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index ba213e5b0..a2c7966d4 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToLmsConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ().ToLMS().Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToLms(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index d7a5deafa..b63f92504 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToRgbConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(RGBWorkingSpaces.sRGB.WhitePoint).ToRGB(RGBWorkingSpaces.sRGB).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).R; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).R; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToRgb(CieXyz).R; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).R; } diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 48b4880d9..6cd8df3fc 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class RgbWorkingSpaceAdapt { - private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); + private static readonly Rgb Rgb = new(0.206162F, 0.260277F, 0.746717F); - private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717); + private static readonly RGBColor RGBColor = new(0.206162, 0.260277, 0.746717); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }); + private static readonly ColorProfileConverter ColorProfileConverter = new(new ColorConversionOptions { RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build(); [Benchmark(Baseline = true, Description = "Colourful Adapt")] - public RGBColor ColourfulConvert() - { - return ColourfulConverter.Convert(RGBColor); - } + public RGBColor ColourfulConvert() => ColourfulConverter.Convert(RGBColor); [Benchmark(Description = "ImageSharp Adapt")] - public Rgb ColorSpaceConvert() - { - return ColorSpaceConverter.Adapt(Rgb); - } + public Rgb ColorSpaceConvert() => ColorProfileConverter.Convert(Rgb); } diff --git a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs index 14d848bcb..093397ad5 100644 --- a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs +++ b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs @@ -11,9 +11,9 @@ public class YcbCrToRgb [Benchmark(Baseline = true, Description = "Floating Point Conversion")] public Vector3 YcbCrToRgba() { - int y = 255; - int cb = 128; - int cr = 128; + const int y = 255; + const int cb = 128; + const int cr = 128; int ccb = cb - 128; int ccr = cr - 128; @@ -28,9 +28,9 @@ public class YcbCrToRgb [Benchmark(Description = "Scaled Integer Conversion")] public Vector3 YcbCrToRgbaScaled() { - int y = 255; - int cb = 128; - int cr = 128; + const int y = 255; + const int cb = 128; + const int cr = 128; int ccb = cb - 128; int ccr = cr - 128; From fb50f5be71605e3b741f0d201ba37ca9d6a0ac8c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:41:54 +1000 Subject: [PATCH 144/220] Replace old converter --- ...rProfileConverterExtensionsCieLabCieLab.cs | 4 +- ...rProfileConverterExtensionsCieLabCieXyz.cs | 4 +- ...olorProfileConverterExtensionsCieLabRgb.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 4 +- ...olorProfileConverterExtensionsCieXyzRgb.cs | 4 +- ...olorProfileConverterExtensionsRgbCieLab.cs | 4 +- ...olorProfileConverterExtensionsRgbCieXyz.cs | 4 +- .../ColorProfileConverterExtensionsRgbRgb.cs | 4 +- .../CieLabPlanarTiffColor{TPixel}.cs | 7 +-- .../CieLabTiffColor{TPixel}.cs | 7 +-- .../CmykTiffColor{TPixel}.cs | 6 +- .../PixelFormats/PixelConversionModifiers.cs | 2 +- .../PixelImplementations/Rgb24.cs | 7 ++- .../PixelImplementations/Rgba32.cs | 7 ++- .../PixelFormats/Utils/Vector4Converters.cs | 6 +- ...ProfileConverterChomaticAdaptationTests.cs | 1 - .../ColorProfiles/HslTests.cs | 2 +- .../RgbAndCieXyzConversionTest.cs | 1 - .../Formats/Jpg/JpegColorConverterTests.cs | 59 ++++++++++--------- .../PixelOperations/PixelOperationsTests.cs | 36 +++++------ 21 files changed, 92 insertions(+), 85 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs index 9bfccb62b..41ae4b08f 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 4c86c8793..04937e927 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs index 89e03f3ae..47e4d2a80 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsCieLabRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index de44cb17e..6b1575d04 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index cea7ea73a..8f56a5a66 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -22,7 +22,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsFrom); + return TTo.FromProfileConnectingSpace(options, in pcsFrom); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs index 0d267e56f..9cc0bd943 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs index 50503bb07..415dd94c3 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsRgbCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs index 19d04e953..a13f64577 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs index f7edd4ba4..c1c75dea1 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsRgbRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs index 6be584581..d6fc7c487 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs @@ -2,8 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1.0f / 255.0f; @@ -34,7 +33,7 @@ internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder(in lab); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f)); offset++; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs index e5dc574f7..b0236022b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CieLabTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1f / 255f; /// @@ -30,7 +29,7 @@ internal class CieLabTiffColor : TiffBaseColorDecoder { float l = (data[offset] & 0xFF) * 100f * Inv255; CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]); - Rgb rgb = ColorSpaceConverter.ToRgb(lab); + Rgb rgb = ColorProfileConverter.Convert(in lab); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1f)); offset += 3; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs index 77baa5351..c7fe2ed07 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CmykTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1f / 255f; /// @@ -23,7 +23,7 @@ internal class CmykTiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255); - Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk); + Rgb rgb = ColorProfileConverter.Convert(in cmyk); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f)); offset += 4; diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 25fc74e08..edc04fa7c 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index ac855d47d..0aa7bad23 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.PixelFormats; @@ -54,13 +55,13 @@ public partial struct Rgb24 : IPixel } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// An instance of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Rgb24(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f)); + public static implicit operator Rgb24(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f)); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index fc347c166..049155343 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.PixelFormats; @@ -180,13 +181,13 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// An instance of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Rgba32(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F)); + public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F)); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs index 8f682ae8f..0a0b5660d 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs @@ -3,7 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; namespace SixLabors.ImageSharp.PixelFormats.Utils; @@ -12,6 +12,8 @@ internal static partial class Vector4Converters /// /// Apply modifiers used requested by ToVector4() conversion. /// + /// The span of vectors. + /// The modifier rule. [MethodImpl(InliningOptions.ShortMethod)] internal static void ApplyForwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { @@ -29,6 +31,8 @@ internal static partial class Vector4Converters /// /// Apply modifiers used requested by FromVector4() conversion. /// + /// The span of vectors. + /// The modifier rule. [MethodImpl(InliningOptions.ShortMethod)] internal static void ApplyBackwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { diff --git a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs index 947f153c2..a90e5b9e8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs index 61eb3db66..d18e65117 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs @@ -2,7 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 960dc50fa..c10aa2c3c 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index ef9f48a89..7aabdaa58 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -3,11 +3,10 @@ using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Tests.Colorspaces.Conversion; +using SixLabors.ImageSharp.Tests.ColorProfiles; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit.Abstractions; @@ -24,9 +23,9 @@ public class JpegColorConverterTests private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2; - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision); + private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: Precision); - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorSpaceConverter = new(); public static readonly TheoryData Seeds = new() { 1, 2, 3 }; @@ -38,7 +37,7 @@ public class JpegColorConverterTests [Fact] public void GetConverterThrowsExceptionOnInvalidColorSpace() { - JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); + const JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); Assert.Throws(() => JpegColorConverterBase.GetConverter(invalidColorSpace, 8)); } @@ -46,7 +45,7 @@ public class JpegColorConverterTests public void GetConverterThrowsExceptionOnInvalidPrecision() { // Valid precisions: 8 & 12 bit - int invalidPrecision = 9; + const int invalidPrecision = 9; Assert.Throws(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, invalidPrecision)); } @@ -428,7 +427,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YCbCrArm(8), + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrArm(8), 3, seed, new JpegColorConverterBase.YCbCrScalar(8)); @@ -436,7 +436,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToYCbCrArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YCbCrArm(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrArm(8), 3, seed, new JpegColorConverterBase.YCbCrScalar(8), @@ -502,7 +503,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8), + this.TestConversionToRgb( + new JpegColorConverterBase.GrayscaleArm(8), 1, seed, new JpegColorConverterBase.GrayscaleScalar(8)); @@ -510,7 +512,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToGrayscaleArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayscaleArm(8), 1, seed, new JpegColorConverterBase.GrayscaleScalar(8), @@ -556,7 +559,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromYccKArm64(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YccKArm64(8), + this.TestConversionToRgb( + new JpegColorConverterBase.YccKArm64(8), 4, seed, new JpegColorConverterBase.YccKScalar(8)); @@ -564,7 +568,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToYccKArm64(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YccKArm64(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKArm64(8), 4, seed, new JpegColorConverterBase.YccKScalar(8), @@ -617,9 +622,9 @@ public class JpegColorConverterTests int componentCount, int seed) { - var rnd = new Random(seed); + Random rnd = new(seed); - var buffers = new Buffer2D[componentCount]; + Buffer2D[] buffers = new Buffer2D[componentCount]; for (int i = 0; i < componentCount; i++) { float[] values = new float[length]; @@ -630,8 +635,8 @@ public class JpegColorConverterTests } // no need to dispose when buffer is not array owner - var memory = new Memory(values); - var source = MemoryGroup.Wrap(memory); + Memory memory = new(values); + MemoryGroup source = MemoryGroup.Wrap(memory); buffers[i] = new Buffer2D(source, values.Length, 1); } @@ -786,9 +791,9 @@ public class JpegColorConverterTests float y = values.Component0[i]; float cb = values.Component1[i]; float cr = values.Component2[i]; - Rgb expected = ColorSpaceConverter.ToRgb(new YCbCr(y, cb, cr)); + Rgb expected = ColorSpaceConverter.Convert(new YCbCr(y, cb, cr)); - Rgb actual = new(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -810,9 +815,9 @@ public class JpegColorConverterTests r /= MaxColorChannelValue; g /= MaxColorChannelValue; b /= MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -823,9 +828,9 @@ public class JpegColorConverterTests float r = values.Component0[i] / MaxColorChannelValue; float g = values.Component1[i] / MaxColorChannelValue; float b = values.Component2[i] / MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -834,9 +839,9 @@ public class JpegColorConverterTests private static void ValidateGrayScale(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) { float y = values.Component0[i] / MaxColorChannelValue; - var expected = new Rgb(y, y, y); + Rgb expected = Rgb.Clamp(new(y, y, y)); - var actual = new Rgb(result.Component0[i], result.Component0[i], result.Component0[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component0[i], result.Component0[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -852,9 +857,9 @@ public class JpegColorConverterTests float r = c * k / MaxColorChannelValue; float g = m * k / MaxColorChannelValue; float b = y * k / MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 68c282d8a..32b62fc03 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -5,7 +5,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Common; @@ -161,12 +161,12 @@ public abstract class PixelOperationsTests : MeasureFixture [MemberData(nameof(ArraySizesData))] public void FromCompandedScaledVector4(int count) { - void SourceAction(ref Vector4 v) => SRgbCompanding.Expand(ref v); + void SourceAction(ref Vector4 v) => v = SRgbCompanding.Expand(v); - void ExpectedAction(ref Vector4 v) => SRgbCompanding.Compress(ref v); + void ExpectedAction(ref Vector4 v) => v = SRgbCompanding.Compress(v); - Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => SourceAction(ref v)); - TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => ExpectedAction(ref v)); + Vector4[] source = CreateVector4TestData(count, SourceAction); + TPixel[] expected = CreateScaledExpectedPixelData(source, ExpectedAction); TestOperation( source, @@ -261,7 +261,7 @@ public abstract class PixelOperationsTests : MeasureFixture { void SourceAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); + v = SRgbCompanding.Expand(v); if (this.HasUnassociatedAlpha) { @@ -276,11 +276,11 @@ public abstract class PixelOperationsTests : MeasureFixture Numerics.UnPremultiply(ref v); } - SRgbCompanding.Compress(ref v); + v = SRgbCompanding.Compress(v); } - Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => SourceAction(ref v)); - TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => ExpectedAction(ref v)); + Vector4[] source = CreateVector4TestData(count, SourceAction); + TPixel[] expected = CreateScaledExpectedPixelData(source, ExpectedAction); TestOperation( source, @@ -385,10 +385,10 @@ public abstract class PixelOperationsTests : MeasureFixture { } - void ExpectedAction(ref Vector4 v) => SRgbCompanding.Expand(ref v); + void ExpectedAction(ref Vector4 v) => v = SRgbCompanding.Expand(v); - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedScaledVector4Data(source, ExpectedAction); TestOperation( source, @@ -410,8 +410,8 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); - TPixel[] source = CreatePixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreatePixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedVector4Data(source, ExpectedAction); TestOperation( source, @@ -429,7 +429,7 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); TestOperation( @@ -452,12 +452,12 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); + v = SRgbCompanding.Expand(v); Numerics.Premultiply(ref v); } - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedScaledVector4Data(source, ExpectedAction); TestOperation( source, From cb56bae39300ab97febdef43335c4c8d15929515 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:44:11 +1000 Subject: [PATCH 145/220] Remove old implementation --- src/ImageSharp/ColorSpaces/CieLab.cs | 135 ------ src/ImageSharp/ColorSpaces/CieLch.cs | 159 ------ src/ImageSharp/ColorSpaces/CieLchuv.cs | 156 ------ src/ImageSharp/ColorSpaces/CieLuv.cs | 136 ------ src/ImageSharp/ColorSpaces/CieXyy.cs | 99 ---- src/ImageSharp/ColorSpaces/CieXyz.cs | 102 ---- src/ImageSharp/ColorSpaces/Cmyk.cs | 107 ---- .../ColorSpaces/Companding/GammaCompanding.cs | 34 -- .../ColorSpaces/Companding/LCompanding.cs | 36 -- .../Companding/Rec2020Companding.cs | 39 -- .../Companding/Rec709Companding.cs | 35 -- .../ColorSpaces/Companding/SRgbCompanding.cs | 230 --------- .../ColorSpaces/Conversion/CieConstants.cs | 21 - .../Conversion/ColorSpaceConverter.Adapt.cs | 159 ------ .../Conversion/ColorSpaceConverter.CieLab.cs | 441 ----------------- .../Conversion/ColorSpaceConverter.CieLch.cs | 441 ----------------- .../ColorSpaceConverter.CieLchuv.cs | 441 ----------------- .../Conversion/ColorSpaceConverter.CieLuv.cs | 435 ----------------- .../Conversion/ColorSpaceConverter.CieXyy.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.CieXyz.cs | 458 ------------------ .../Conversion/ColorSpaceConverter.Cmyk.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.Hsl.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.Hsv.cs | 437 ----------------- .../ColorSpaceConverter.HunterLab.cs | 430 ---------------- .../ColorSpaceConverter.LinearRgb.cs | 429 ---------------- .../Conversion/ColorSpaceConverter.Lms.cs | 425 ---------------- .../Conversion/ColorSpaceConverter.Rgb.cs | 419 ---------------- .../Conversion/ColorSpaceConverter.YCbCr.cs | 404 --------------- .../Conversion/ColorSpaceConverter.cs | 59 --- .../Conversion/ColorSpaceConverterOptions.cs | 53 -- .../CieXyChromaticityCoordinates.cs | 83 ---- .../Converters/CIeLchToCieLabConverter.cs | 31 -- .../Converters/CieLabToCieLchConverter.cs | 39 -- .../Converters/CieLabToCieXyzConverter.cs | 43 -- .../Converters/CieLchuvToCieLuvConverter.cs | 31 -- .../Converters/CieLuvToCieLchuvConverter.cs | 39 -- .../Converters/CieLuvToCieXyzConverter.cs | 73 --- .../Converters/CieXyzAndCieXyyConverter.cs | 52 -- .../CieXyzAndHunterLabConverterBase.cs | 44 -- .../Converters/CieXyzAndLmsConverter.cs | 69 --- .../Converters/CieXyzToCieLabConverter.cs | 57 --- .../Converters/CieXyzToCieLuvConverter.cs | 86 ---- .../Converters/CieXyzToHunterLabConverter.cs | 65 --- .../Converters/CieXyzToLinearRgbConverter.cs | 55 --- .../Converters/CmykAndRgbConverter.cs | 49 -- .../Converters/HslAndRgbConverter.cs | 158 ------ .../Converters/HsvAndRgbConverter.cs | 128 ----- .../Converters/HunterLabToCieXyzConverter.cs | 37 -- .../LinearRgbAndCieXyzConverterBase.cs | 73 --- .../Converters/LinearRgbToCieXyzConverter.cs | 52 -- .../Converters/LinearRgbToRgbConverter.cs | 25 - .../Converters/RgbToLinearRgbConverter.cs | 25 - .../Converters/YCbCrAndRgbConverter.cs | 55 --- .../Implementation/IChromaticAdaptation.cs | 36 -- .../Implementation/LmsAdaptationMatrix.cs | 133 ----- .../RGBPrimariesChromaticityCoordinates.cs | 88 ---- .../VonKriesChromaticAdaptation.cs | 99 ---- .../WorkingSpaces/GammaWorkingSpace.cs | 64 --- .../WorkingSpaces/LWorkingSpace.cs | 31 -- .../WorkingSpaces/Rec2020WorkingSpace.cs | 31 -- .../WorkingSpaces/Rec709WorkingSpace.cs | 31 -- .../WorkingSpaces/RgbWorkingSpace.cs | 79 --- .../WorkingSpaces/SRgbWorkingSpace.cs | 31 -- src/ImageSharp/ColorSpaces/Hsl.cs | 100 ---- src/ImageSharp/ColorSpaces/Hsv.cs | 98 ---- src/ImageSharp/ColorSpaces/HunterLab.cs | 134 ----- src/ImageSharp/ColorSpaces/Illuminants.cs | 71 --- src/ImageSharp/ColorSpaces/LinearRgb.cs | 142 ------ src/ImageSharp/ColorSpaces/Lms.cs | 103 ---- src/ImageSharp/ColorSpaces/Rgb.cs | 163 ------- .../ColorSpaces/RgbWorkingSpaces.cs | 114 ----- src/ImageSharp/ColorSpaces/YCbCr.cs | 99 ---- .../Colorspaces/CieLabTests.cs | 44 -- .../Colorspaces/CieLchTests.cs | 42 -- .../Colorspaces/CieLchuvTests.cs | 42 -- .../Colorspaces/CieLuvTests.cs | 42 -- .../CieXyChromaticityCoordinatesTests.cs | 41 -- .../Colorspaces/CieXyyTests.cs | 42 -- .../Colorspaces/CieXyzTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/CmykTests.cs | 44 -- .../Colorspaces/Companding/CompandingTests.cs | 113 ----- .../ApproximateColorspaceComparer.cs | 238 --------- .../CieLabAndCieLchConversionTests.cs | 92 ---- .../CieLabAndCieLchuvConversionTests.cs | 80 --- .../CieLabAndCieLuvConversionTests.cs | 80 --- .../CieLabAndCieXyyConversionTests.cs | 76 --- .../CieLabAndCmykConversionTests.cs | 76 --- .../Conversion/CieLabAndHslConversionTests.cs | 76 --- .../Conversion/CieLabAndHsvConversionTests.cs | 76 --- .../CieLabAndHunterLabConversionTests.cs | 76 --- .../CieLabAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieLabAndLmsConversionTests.cs | 76 --- .../Conversion/CieLabAndRgbConversionTests.cs | 76 --- .../CieLabAndYCbCrConversionTests.cs | 76 --- .../CieLchAndCieLuvConversionTests.cs | 75 --- .../CieLchAndCieXyyConversionTests.cs | 75 --- .../Conversion/CieLchAndHslConversionTests.cs | 75 --- .../Conversion/CieLchAndHsvConversionTests.cs | 75 --- .../CieLchAndHunterLabConversionTests.cs | 75 --- .../CieLchAndLinearRgbConversionTests.cs | 75 --- .../Conversion/CieLchAndLmsConversionTests.cs | 75 --- .../Conversion/CieLchAndRgbConversionTests.cs | 75 --- .../CieLchAndYCbCrConversionTests.cs | 75 --- .../CieLchuvAndCieLchConversionTests.cs | 75 --- .../CieLchuvAndCieLuvConversionTests.cs | 93 ---- .../CieLchuvAndCmykConversionTests.cs | 76 --- .../CieLuvAndCieXyyConversionTests.cs | 76 --- .../Conversion/CieLuvAndHslConversionTests.cs | 76 --- .../Conversion/CieLuvAndHsvConversionTests.cs | 76 --- .../CieLuvAndHunterLabConversionTests.cs | 76 --- .../CieLuvAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieLuvAndLmsConversionTests.cs | 76 --- .../Conversion/CieLuvAndRgbConversionTests.cs | 76 --- .../CieLuvAndYCbCrConversionTests.cs | 76 --- .../Conversion/CieXyyAndHslConversionTests.cs | 76 --- .../Conversion/CieXyyAndHsvConversionTests.cs | 76 --- .../CieXyyAndHunterLabConversionTests.cs | 76 --- .../CieXyyAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieXyyAndLmsConversionTests.cs | 76 --- .../Conversion/CieXyyAndRgbConversionTests.cs | 76 --- .../CieXyyAndYCbCrConversionTests.cs | 76 --- .../CieXyzAndCieLabConversionTest.cs | 93 ---- .../CieXyzAndCieLchConversionTests.cs | 75 --- .../CieXyzAndCieLchuvConversionTests.cs | 75 --- .../CieXyzAndCieLuvConversionTest.cs | 92 ---- .../CieXyzAndCieXyyConversionTest.cs | 76 --- .../Conversion/CieXyzAndHslConversionTests.cs | 76 --- .../Conversion/CieXyzAndHsvConversionTests.cs | 76 --- .../CieXyzAndHunterLabConversionTest.cs | 115 ----- .../Conversion/CieXyzAndLmsConversionTest.cs | 88 ---- .../CieXyzAndYCbCrConversionTests.cs | 76 --- .../CmykAndCieLchConversionTests.cs | 75 --- .../CmykAndCieLuvConversionTests.cs | 76 --- .../CmykAndCieXyyConversionTests.cs | 76 --- .../CmykAndCieXyzConversionTests.cs | 76 --- .../Conversion/CmykAndHslConversionTests.cs | 76 --- .../Conversion/CmykAndHsvConversionTests.cs | 76 --- .../CmykAndHunterLabConversionTests.cs | 76 --- .../Conversion/CmykAndYCbCrConversionTests.cs | 76 --- .../Conversion/ColorConverterAdaptTest.cs | 178 ------- .../Conversion/RgbAndCieXyzConversionTest.cs | 170 ------- .../Conversion/RgbAndCmykConversionTest.cs | 84 ---- .../Conversion/RgbAndHslConversionTest.cs | 90 ---- .../Conversion/RgbAndHsvConversionTest.cs | 88 ---- .../Conversion/RgbAndYCbCrConversionTest.cs | 83 ---- .../VonKriesChromaticAdaptationTests.cs | 38 -- .../ImageSharp.Tests/Colorspaces/HslTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/HsvTests.cs | 42 -- .../Colorspaces/HunterLabTests.cs | 43 -- .../Colorspaces/LinearRgbTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/LmsTests.cs | 43 -- .../ImageSharp.Tests/Colorspaces/RgbTests.cs | 81 ---- .../Colorspaces/StringRepresentationTests.cs | 63 --- .../Colorspaces/YCbCrTests.cs | 42 -- 154 files changed, 16910 deletions(-) delete mode 100644 src/ImageSharp/ColorSpaces/CieLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLch.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLchuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieXyy.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieXyz.cs delete mode 100644 src/ImageSharp/ColorSpaces/Cmyk.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/LCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Hsl.cs delete mode 100644 src/ImageSharp/ColorSpaces/Hsv.cs delete mode 100644 src/ImageSharp/ColorSpaces/HunterLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Illuminants.cs delete mode 100644 src/ImageSharp/ColorSpaces/LinearRgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Lms.cs delete mode 100644 src/ImageSharp/ColorSpaces/Rgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs delete mode 100644 src/ImageSharp/ColorSpaces/YCbCr.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CmykTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HslTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HsvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/LmsTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/RgbTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs deleted file mode 100644 index 2346b395c..000000000 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a CIE L*a*b* 1976 color. -/// -/// -public readonly struct CieLab : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(float l, float a, float b) - : this(l, a, b, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(float l, float a, float b, CieXyz whitePoint) - : this(new Vector3(l, a, b), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(Vector3 vector, CieXyz whitePoint) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.A = vector.Y; - this.B = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a color component. - /// A value usually ranging from -100 to 100. Negative is green, positive magenta. - /// - public readonly float A { get; } - - /// - /// Gets the b color component. - /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow - /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLab left, CieLab right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLab other) => - this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs deleted file mode 100644 index 48e8e2c6d..000000000 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. -/// -/// -public readonly struct CieLch : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - - private static readonly Vector3 Min = new(0, -200, 0); - private static readonly Vector3 Max = new(100, 200, 360); - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(float l, float c, float h) - : this(l, c, h, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(float l, float c, float h, CieXyz whitePoint) - : this(new Vector3(l, c, h), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(Vector3 vector, CieXyz whitePoint) - { - vector = Vector3.Clamp(vector, Min, Max); - this.L = vector.X; - this.C = vector.Y; - this.H = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 200. - /// - public readonly float C { get; } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLch left, CieLch right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); - - /// - public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLch other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); - - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } -} diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs deleted file mode 100644 index 4d47be5aa..000000000 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. -/// -/// -public readonly struct CieLchuv : IEquatable -{ - private static readonly Vector3 Min = new(0, -200, 0); - private static readonly Vector3 Max = new(100, 200, 360); - - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(float l, float c, float h) - : this(l, c, h, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(float l, float c, float h, CieXyz whitePoint) - : this(new Vector3(l, c, h), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(Vector3 vector, CieXyz whitePoint) - : this() - { - vector = Vector3.Clamp(vector, Min, Max); - this.L = vector.X; - this.C = vector.Y; - this.H = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 200. - /// - public readonly float C { get; } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLchuv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLchuv other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); - - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } -} diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs deleted file mode 100644 index 04bc96cfa..000000000 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International -/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which -/// attempted perceptual uniformity -/// -/// -public readonly struct CieLuv : IEquatable -{ - /// - /// D65 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The blue-yellow chromaticity coordinate of the given whitepoint. - /// The red-green chromaticity coordinate of the given whitepoint. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(float l, float u, float v) - : this(l, u, v, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The blue-yellow chromaticity coordinate of the given whitepoint. - /// The red-green chromaticity coordinate of the given whitepoint. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(float l, float u, float v, CieXyz whitePoint) - : this(new Vector3(l, u, v), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, u, v components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, u, v components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(Vector3 vector, CieXyz whitePoint) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.U = vector.Y; - this.V = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension - /// A value usually ranging between 0 and 100. - /// - public readonly float L { get; } - - /// - /// Gets the blue-yellow chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public readonly float U { get; } - - /// - /// Gets the red-green chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public readonly float V { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLuv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLuv other) - => this.L.Equals(other.L) - && this.U.Equals(other.U) - && this.V.Equals(other.V) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs deleted file mode 100644 index 6b7d2e6cb..000000000 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CIE xyY 1931 color -/// -/// -public readonly struct CieXyy : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// The x chroma component. - /// The y chroma component. - /// The y luminance component. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyy(float x, float y, float yl) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = x; - this.Y = y; - this.Yl = yl; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the x, y, Y components. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyy(Vector3 vector) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = vector.X; - this.Y = vector.Y; - this.Yl = vector.Z; - } - - /// - /// Gets the X chrominance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float X { get; } - - /// - /// Gets the Y chrominance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Yl { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Yl); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyy other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Yl.Equals(other.Yl); -} diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs deleted file mode 100644 index 2ac9c9f28..000000000 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CIE XYZ 1931 color -/// -/// -public readonly struct CieXyz : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative - /// The y luminance component. - /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the x, y, z components. - public CieXyz(Vector3 vector) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = vector.X; - this.Y = vector.Y; - this.Z = vector.Z; - } - - /// - /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. - /// A value usually ranging between 0 and 1. - /// - public readonly float X { get; } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response. - /// A value usually ranging between 0 and 1. - /// - public readonly float Z { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.X, this.Y, this.Z); - - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyz other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Z.Equals(other.Z); -} diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs deleted file mode 100644 index a5aacf38a..000000000 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CMYK (cyan, magenta, yellow, keyline) color. -/// -public readonly struct Cmyk : IEquatable -{ - private static readonly Vector4 Min = Vector4.Zero; - private static readonly Vector4 Max = Vector4.One; - - /// - /// Initializes a new instance of the struct. - /// - /// The cyan component. - /// The magenta component. - /// The yellow component. - /// The keyline black component. - [MethodImpl(InliningOptions.ShortMethod)] - public Cmyk(float c, float m, float y, float k) - : this(new Vector4(c, m, y, k)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the c, m, y, k components. - [MethodImpl(InliningOptions.ShortMethod)] - public Cmyk(Vector4 vector) - { - vector = Numerics.Clamp(vector, Min, Max); - this.C = vector.X; - this.M = vector.Y; - this.Y = vector.Z; - this.K = vector.W; - } - - /// - /// Gets the cyan color component. - /// A value ranging between 0 and 1. - /// - public readonly float C { get; } - - /// - /// Gets the magenta color component. - /// A value ranging between 0 and 1. - /// - public readonly float M { get; } - - /// - /// Gets the yellow color component. - /// A value ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the keyline black color component. - /// A value ranging between 0 and 1. - /// - public readonly float K { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.C, this.M, this.Y, this.K); - - /// - public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Cmyk other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Cmyk other) - => this.C.Equals(other.C) - && this.M.Equals(other.M) - && this.Y.Equals(other.Y) - && this.K.Equals(other.K); -} diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs deleted file mode 100644 index e5d98430f..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements gamma companding. -/// -/// -/// -/// -/// -public static class GammaCompanding -{ - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The gamma value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel, float gamma) => MathF.Pow(channel, gamma); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The gamma value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel, float gamma) => MathF.Pow(channel, 1 / gamma); -} diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs deleted file mode 100644 index db44fd069..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements L* companding. -/// -/// -/// For more info see: -/// -/// -/// -public static class LCompanding -{ - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : Numerics.Pow3((channel + 0.16F) / 1.16F); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs deleted file mode 100644 index 450097618..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements Rec. 2020 companding function. -/// -/// -/// -/// -public static class Rec2020Companding -{ - private const float Alpha = 1.09929682680944F; - private const float AlphaMinusOne = Alpha - 1F; - private const float Beta = 0.018053968510807F; - private const float InverseBeta = Beta * 4.5F; - private const float Epsilon = 1 / 0.45F; - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel < InverseBeta ? channel / 4.5F : MathF.Pow((channel + AlphaMinusOne) / Alpha, Epsilon); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel < Beta ? 4.5F * channel : (Alpha * MathF.Pow(channel, 0.45F)) - AlphaMinusOne; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs deleted file mode 100644 index 6e4767bcd..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements the Rec. 709 companding function. -/// -/// -/// http://en.wikipedia.org/wiki/Rec._709 -/// -public static class Rec709Companding -{ - private const float Epsilon = 1 / 0.45F; - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, Epsilon); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel < 0.018F ? 4.5F * channel : (1.099F * MathF.Pow(channel, 0.45F)) - 0.099F; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs deleted file mode 100644 index 4c3923c88..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements sRGB companding. -/// -/// -/// For more info see: -/// -/// -/// -public static class SRgbCompanding -{ - private const int Length = Scale + 2; // 256kb @ 16bit precision. - private const int Scale = (1 << 16) - 1; - - private static readonly Lazy LazyCompressTable = new( - () => - { - float[] result = new float[Length]; - - for (int i = 0; i < result.Length; i++) - { - double d = (double)i / Scale; - if (d <= (0.04045 / 12.92)) - { - d *= 12.92; - } - else - { - d = (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055; - } - - result[i] = (float)d; - } - - return result; - }, - true); - - private static readonly Lazy LazyExpandTable = new( - () => - { - float[] result = new float[Length]; - - for (int i = 0; i < result.Length; i++) - { - double d = (double)i / Scale; - if (d <= 0.04045) - { - d /= 12.92; - } - else - { - d = Math.Pow((d + 0.055) / 1.055, 2.4); - } - - result[i] = (float)d; - } - - return result; - }, - true); - - private static float[] ExpandTable => LazyExpandTable.Value; - - private static float[] CompressTable => LazyCompressTable.Value; - - /// - /// Expands the companded vectors to their linear equivalents with respect to the energy. - /// - /// The span of vectors. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Expand(Span vectors) - { - if (Avx2.IsSupported && vectors.Length >= 2) - { - CompandAvx2(vectors, ExpandTable); - - if (Numerics.Modulo2(vectors.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - Expand(ref MemoryMarshal.GetReference(vectors[^1..])); - } - } - else - { - CompandScalar(vectors, ExpandTable); - } - } - - /// - /// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy. - /// - /// The span of vectors. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Compress(Span vectors) - { - if (Avx2.IsSupported && vectors.Length >= 2) - { - CompandAvx2(vectors, CompressTable); - - if (Numerics.Modulo2(vectors.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - Compress(ref MemoryMarshal.GetReference(vectors[^1..])); - } - } - else - { - CompandScalar(vectors, CompressTable); - } - } - - /// - /// Expands a companded vector to its linear equivalent with respect to the energy. - /// - /// The vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Expand(ref Vector4 vector) - { - // Alpha is already a linear representation of opacity so we do not want to convert it. - vector.X = Expand(vector.X); - vector.Y = Expand(vector.Y); - vector.Z = Expand(vector.Z); - } - - /// - /// Compresses an uncompanded vector (linear) to its nonlinear equivalent. - /// - /// The vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Compress(ref Vector4 vector) - { - // Alpha is already a linear representation of opacity so we do not want to convert it. - vector.X = Compress(vector.X); - vector.Y = Compress(vector.Y); - vector.Z = Compress(vector.Z); - } - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Expand(float channel) - => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Compress(float channel) - => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CompandAvx2(Span vectors, float[] table) - { - fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) - { - var scale = Vector256.Create((float)Scale); - Vector256 zero = Vector256.Zero; - var offset = Vector256.Create(1); - - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); - - while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) - { - Vector256 multiplied = Avx.Multiply(scale, vectorsBase); - multiplied = Avx.Min(Avx.Max(zero, multiplied), scale); - - Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied); - Vector256 truncatedF = Avx.ConvertToVector256Single(truncated); - - Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float)); - Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float)); - - // Alpha is already a linear representation of opacity so we do not want to convert it. - Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF)); - vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl); - vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CompandScalar(Span vectors, float[] table) - { - fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) - { - Vector4 zero = Vector4.Zero; - var scale = new Vector4(Scale); - ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length); - - while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) - { - Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale); - - float f0 = multiplied.X; - float f1 = multiplied.Y; - float f2 = multiplied.Z; - - uint i0 = (uint)f0; - uint i1 = (uint)f1; - uint i2 = (uint)f2; - - // Alpha is already a linear representation of opacity so we do not want to convert it. - vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0); - vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1); - vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2); - - vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); - } - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs deleted file mode 100644 index 7c8794404..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Constants use for Cie conversion calculations -/// -/// -internal static class CieConstants -{ - /// - /// 216F / 24389F - /// - public const float Epsilon = 0.008856452F; - - /// - /// 24389F / 27F - /// - public const float Kappa = 903.2963F; -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs deleted file mode 100644 index b4934e44a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Performs chromatic adaptation on the various color spaces. -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs chromatic adaptation of given color. - /// Target white point is . - /// - /// The color to adapt - /// The source white point. - /// The adapted color - public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) => this.Adapt(color, sourceWhitePoint, this.whitePoint); - - /// - /// Performs chromatic adaptation of given color. - /// Target white point is . - /// - /// The color to adapt - /// The source white point. - /// The target white point. - /// The adapted color - public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) - { - if (!this.performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint)) - { - return color; - } - - // We know that chromaticAdaption is not null because performChromaticAdaption is checked - return this.chromaticAdaptation!.Transform(color, sourceWhitePoint, targetWhitePoint); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLab Adapt(in CieLab color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLch Adapt(in CieLch color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLchuv Adapt(in CieLchuv color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var luvColor = this.ToCieLuv(color); - return this.ToCieLchuv(luvColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLuv Adapt(in CieLuv color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public HunterLab Adapt(in HunterLab color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Adapts a color from the source working space to working space set in . - /// - /// The color to adapt - /// The adapted color - public LinearRgb Adapt(in LinearRgb color) - { - if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace)) - { - return color; - } - - // Conversion to XYZ - LinearRgbToCieXyzConverter converterToXYZ = GetLinearRgbToCieXyzConverter(color.WorkingSpace); - CieXyz unadapted = converterToXYZ.Convert(color); - - // Adaptation - // We know that chromaticAdaption is not null because performChromaticAdaption is checked - CieXyz adapted = this.chromaticAdaptation!.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint); - - // Conversion back to RGB - return this.cieXyzToLinearRgbConverter.Convert(adapted); - } - - /// - /// Adapts an color from the source working space to working space set in . - /// - /// The color to adapt - /// The adapted color - public Rgb Adapt(in Rgb color) - { - if (!this.performChromaticAdaptation) - { - return color; - } - - var linearInput = ToLinearRgb(color); - LinearRgb linearOutput = this.Adapt(linearInput); - return ToRgb(linearOutput); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs deleted file mode 100644 index 54667ca2a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLch color) - { - // Conversion (preserving white point) - CieLab unadapted = CieLchToCieLabConverter.Convert(color); - - return this.Adapt(unadapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieXyz color) - { - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint); - - return this.cieXyzToCieLabConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs deleted file mode 100644 index 9949b5d91..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLab color) - { - CieLab adapted = this.Adapt(color); - - return CieLabToCieLchConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieXyz color) - { - var labColor = this.ToCieLab(color); - - return this.ToCieLch(labColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in HunterLab color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Lms color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs deleted file mode 100644 index 4b856d118..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLuv color) - { - CieLuv adapted = this.Adapt(color); - - return CieLuvToCieLchuvConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieXyz color) - { - CieLuv luvColor = this.ToCieLuv(color); - - return this.ToCieLchuv(luvColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs deleted file mode 100644 index 2e8029f64..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLchuv color) - { - // Conversion (preserving white point) - CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieXyz color) - { - // Adaptation - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLuvWhitePoint); - - // Conversion - return this.cieXyzToCieLuvConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs deleted file mode 100644 index 13b2a225c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs deleted file mode 100644 index 2212ca2e5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - private static readonly ConcurrentDictionary ConverterCache = new(); - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLab color) - { - // Conversion - CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLch color) - { - // Conversion to Lab - CieLab labColor = CieLchToCieLabConverter.Convert(color); - - // Conversion to XYZ (incl. adaptation) - return this.ToCieXyz(labColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLchuv color) - { - // Conversion to Luv - CieLuv luvColor = CieLchuvToCieLuvConverter.Convert(color); - - // Conversion to XYZ (incl. adaptation) - return this.ToCieXyz(luvColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLuv color) - { - // Conversion - CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static CieXyz ToCieXyz(in CieXyy color) - - // Conversion - => CieXyzAndCieXyyConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Hsv color) - { - // Conversion - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in HunterLab color) - { - CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); - - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in LinearRgb color) - { - // Conversion - LinearRgbToCieXyzConverter converter = GetLinearRgbToCieXyzConverter(color.WorkingSpace); - CieXyz unadapted = converter.Convert(color); - - return this.Adapt(unadapted, color.WorkingSpace.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Lms color) - => this.cieXyzAndLmsConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Rgb color) - { - // Conversion - LinearRgb linear = RgbToLinearRgbConverter.Convert(color); - return this.ToCieXyz(linear); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Gets the correct converter for the given rgb working space. - /// - /// The source working space - /// The - private static LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) - => ConverterCache.GetOrAdd(workingSpace, (key) => new LinearRgbToCieXyzConverter(key)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs deleted file mode 100644 index ea9a5d734..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors, - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs deleted file mode 100644 index 67ec16291..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs deleted file mode 100644 index 47ee42dc5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs deleted file mode 100644 index 060402776..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLch color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieXyz color) - { - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint); - - return this.cieXyzToHunterLabConverter.Convert(adapted); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Lms color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs deleted file mode 100644 index fd385a15b..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieXyz color) - { - // Adaptation - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint); - - // Conversion - return this.cieXyzToLinearRgbConverter.Convert(adapted); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Cmyk color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Hsl color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Hsv color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Rgb color) - => RgbToLinearRgbConverter.Convert(color); - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - return ToLinearRgb(rgb); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs deleted file mode 100644 index 56f61ef80..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLch color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieXyz color) => this.cieXyzAndLmsConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in HunterLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs deleted file mode 100644 index 080e1fc4b..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieXyz color) - { - // Conversion - LinearRgb linear = this.ToLinearRgb(color); - - // Compand - return ToRgb(linear); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Cmyk color) => CmykAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Hsv color) => HsvAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Hsl color) => HslAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in LinearRgb color) => LinearRgbToRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in YCbCr color) - { - // Conversion - Rgb rgb = YCbCrAndRgbConverter.Convert(color); - - // Adaptation - return this.Adapt(rgb); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs deleted file mode 100644 index da8e046ff..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs deleted file mode 100644 index b5e3162e6..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Provides methods to allow the conversion of color values between different color spaces. -/// -public partial class ColorSpaceConverter -{ - // Options. - private static readonly ColorSpaceConverterOptions DefaultOptions = new(); - private readonly Matrix4x4 lmsAdaptationMatrix; - private readonly CieXyz whitePoint; - private readonly CieXyz targetLuvWhitePoint; - private readonly CieXyz targetLabWhitePoint; - private readonly CieXyz targetHunterLabWhitePoint; - private readonly RgbWorkingSpace targetRgbWorkingSpace; - private readonly IChromaticAdaptation? chromaticAdaptation; - private readonly bool performChromaticAdaptation; - private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; - private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter; - private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter; - private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter; - private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; - - /// - /// Initializes a new instance of the class. - /// - public ColorSpaceConverter() - : this(DefaultOptions) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration options. - public ColorSpaceConverter(ColorSpaceConverterOptions options) - { - Guard.NotNull(options, nameof(options)); - this.whitePoint = options.WhitePoint; - this.targetLuvWhitePoint = options.TargetLuvWhitePoint; - this.targetLabWhitePoint = options.TargetLabWhitePoint; - this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint; - this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace; - this.chromaticAdaptation = options.ChromaticAdaptation; - this.performChromaticAdaptation = this.chromaticAdaptation != null; - this.lmsAdaptationMatrix = options.LmsAdaptationMatrix; - - this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix); - this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint); - this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint); - this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint); - this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs deleted file mode 100644 index 9f576de72..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Configuration options for the class. -/// -public class ColorSpaceConverterOptions -{ - /// - /// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. - /// When default, no adaptation will be performed. - /// Defaults to: . - /// - public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint; - - /// - /// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint; - - /// - /// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint; - - /// - /// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint; - - /// - /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) - /// Defaults to: . - /// - public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; - - /// - /// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed. - /// - public IChromaticAdaptation? ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation(); - - /// - /// Gets or sets transformation matrix used in conversion to and from . - /// - public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix; -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs deleted file mode 100644 index 2cc785d53..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -// ReSharper disable CompareOfFloatsByEqualityOperator -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Represents the coordinates of CIEXY chromaticity space. -/// -public readonly struct CieXyChromaticityCoordinates : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// Chromaticity coordinate x (usually from 0 to 1) - /// Chromaticity coordinate y (usually from 0 to 1) - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyChromaticityCoordinates(float x, float y) - { - this.X = x; - this.Y = y; - } - - /// - /// Gets the chromaticity X-coordinate. - /// - /// - /// Ranges usually from 0 to 1. - /// - public readonly float X { get; } - - /// - /// Gets the chromaticity Y-coordinate - /// - /// - /// Ranges usually from 0 to 1. - /// - public readonly float Y { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() - => HashCode.Combine(this.X, this.Y); - - /// - public override string ToString() - => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); - - /// - public override bool Equals(object? obj) - => obj is CieXyChromaticityCoordinates other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyChromaticityCoordinates other) - => this.X.Equals(other.X) && this.Y.Equals(other.Y); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs deleted file mode 100644 index f16ce7b0f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLchToCieLabConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLab Convert(in CieLch input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC - float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); - - float a = c * MathF.Cos(hRadians); - float b = c * MathF.Sin(hRadians); - - return new CieLab(l, a, b, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs deleted file mode 100644 index 4eeb7695e..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLabToCieLchConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLch Convert(in CieLab input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC - float l = input.L, a = input.A, b = input.B; - float c = MathF.Sqrt((a * a) + (b * b)); - float hRadians = MathF.Atan2(b, a); - float hDegrees = GeometryUtilities.RadianToDegree(hRadians); - - // Wrap the angle round at 360. - hDegrees %= 360; - - // Make sure it's not negative. - while (hDegrees < 0) - { - hDegrees += 360; - } - - return new CieLch(l, c, hDegrees, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs deleted file mode 100644 index b02ad0000..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLabToCieXyzConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in CieLab input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html - float l = input.L, a = input.A, b = input.B; - float fy = (l + 16) / 116F; - float fx = (a / 500F) + fy; - float fz = fy - (b / 200F); - - float fx3 = Numerics.Pow3(fx); - float fz3 = Numerics.Pow3(fz); - - float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; - float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa; - float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa; - - Vector3 wxyz = new(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z); - - // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) - Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); - - Vector3 xyz = xyzr * wxyz; - return new CieXyz(xyz); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs deleted file mode 100644 index 9a1e79da4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLchuvToCieLuvConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLuv Convert(in CieLchuv input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 - float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); - - float u = c * MathF.Cos(hRadians); - float v = c * MathF.Sin(hRadians); - - return new CieLuv(l, u, v, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs deleted file mode 100644 index 4756bab82..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLuvToCieLchuvConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLchuv Convert(in CieLuv input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 - float l = input.L, a = input.U, b = input.V; - float c = MathF.Sqrt((a * a) + (b * b)); - float hRadians = MathF.Atan2(b, a); - float hDegrees = GeometryUtilities.RadianToDegree(hRadians); - - // Wrap the angle round at 360. - hDegrees %= 360; - - // Make sure it's not negative. - while (hDegrees < 0) - { - hDegrees += 360; - } - - return new CieLchuv(l, c, hDegrees, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs deleted file mode 100644 index 404c4e824..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLuvToCieXyzConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - public static CieXyz Convert(in CieLuv input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html - float l = input.L, u = input.U, v = input.V; - - float u0 = ComputeU0(input.WhitePoint); - float v0 = ComputeV0(input.WhitePoint); - - float y = l > CieConstants.Kappa * CieConstants.Epsilon - ? Numerics.Pow3((l + 16) / 116) - : l / CieConstants.Kappa; - - float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; - float b = -5 * y; - const float c = -0.3333333F; - float d = y * ((39 * l / (v + (13 * l * v0))) - 5); - - float x = (d - b) / (a - c); - float z = (x * a) + b; - - if (float.IsNaN(x) || x < 0) - { - x = 0; - } - - if (float.IsNaN(y) || y < 0) - { - y = 0; - } - - if (float.IsNaN(z) || z < 0) - { - z = 0; - } - - return new CieXyz(x, y, z); - } - - /// - /// Calculates the blue-yellow chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeU0(in CieXyz input) - => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - - /// - /// Calculates the red-green chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeV0(in CieXyz input) - => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs deleted file mode 100644 index 4cc443cf7..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between CIE XYZ and CIE xyY. -/// for formulas. -/// -internal static class CieXyzAndCieXyyConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyy Convert(in CieXyz input) - { - float x = input.X / (input.X + input.Y + input.Z); - float y = input.Y / (input.X + input.Y + input.Z); - - if (float.IsNaN(x) || float.IsNaN(y)) - { - return new CieXyy(0, 0, input.Y); - } - - return new CieXyy(x, y, input.Y); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in CieXyy input) - { - if (MathF.Abs(input.Y) < Constants.Epsilon) - { - return new CieXyz(0, 0, input.Yl); - } - - float x = (input.X * input.Yl) / input.Y; - float y = input.Yl; - float z = ((1 - input.X - input.Y) * y) / input.Y; - - return new CieXyz(x, y, z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs deleted file mode 100644 index ba221108f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The base class for converting between and color spaces. -/// -internal abstract class CieXyzAndHunterLabConverterBase -{ - /// - /// Returns the Ka coefficient that depends upon the whitepoint illuminant. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public static float ComputeKa(CieXyz whitePoint) - { - if (whitePoint.Equals(Illuminants.C)) - { - return 175F; - } - - return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y); - } - - /// - /// Returns the Kb coefficient that depends upon the whitepoint illuminant. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public static float ComputeKb(CieXyz whitePoint) - { - if (whitePoint == Illuminants.C) - { - return 70F; - } - - return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs deleted file mode 100644 index b5f8a70b6..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzAndLmsConverter -{ - /// - /// Default transformation matrix used, when no other is set. (Bradford) - /// - /// - public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; - - private Matrix4x4 inverseTransformationMatrix; - private Matrix4x4 transformationMatrix; - - /// - /// Initializes a new instance of the class. - /// - public CieXyzAndLmsConverter() - : this(DefaultTransformationMatrix) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Definition of the cone response domain (see ), - /// if not set will be used. - /// - public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) - { - this.transformationMatrix = transformationMatrix; - Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public Lms Convert(in CieXyz input) - { - Vector3 vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix); - - return new Lms(vector); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz Convert(in Lms input) - { - Vector3 vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix); - - return new CieXyz(vector); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs deleted file mode 100644 index 0ce6e3c9f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal sealed class CieXyzToCieLabConverter -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToCieLabConverter() - : this(CieLab.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target reference lab white point - public CieXyzToCieLabConverter(CieXyz labWhitePoint) => this.LabWhitePoint = labWhitePoint; - - /// - /// Gets the target reference whitepoint. When not set, is used. - /// - public CieXyz LabWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab Convert(in CieXyz input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html - float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z; - - float xr = input.X / wx, yr = input.Y / wy, zr = input.Z / wz; - - const float inv116 = 1 / 116F; - - float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) * inv116; - float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) * inv116; - float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) * inv116; - - float l = (116F * fy) - 16F; - float a = 500F * (fx - fy); - float b = 200F * (fy - fz); - - return new CieLab(l, a, b, this.LabWhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs deleted file mode 100644 index 1e17ae54f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal sealed class CieXyzToCieLuvConverter -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToCieLuvConverter() - : this(CieLuv.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target reference luv white point - public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) => this.LuvWhitePoint = luvWhitePoint; - - /// - /// Gets the target reference whitepoint. When not set, is used. - /// - public CieXyz LuvWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - public CieLuv Convert(in CieXyz input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html - float yr = input.Y / this.LuvWhitePoint.Y; - float up = ComputeUp(input); - float vp = ComputeVp(input); - float upr = ComputeUp(this.LuvWhitePoint); - float vpr = ComputeVp(this.LuvWhitePoint); - - float l = yr > CieConstants.Epsilon ? ((116 * MathF.Pow(yr, 0.3333333F)) - 16F) : (CieConstants.Kappa * yr); - - if (float.IsNaN(l) || l < 0) - { - l = 0; - } - - float u = 13 * l * (up - upr); - float v = 13 * l * (vp - vpr); - - if (float.IsNaN(u)) - { - u = 0; - } - - if (float.IsNaN(v)) - { - v = 0; - } - - return new CieLuv(l, u, v, this.LuvWhitePoint); - } - - /// - /// Calculates the blue-yellow chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float ComputeUp(in CieXyz input) - => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - - /// - /// Calculates the red-green chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeVp(in CieXyz input) - => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs deleted file mode 100644 index dab953a74..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToHunterLabConverter() - : this(HunterLab.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The hunter Lab white point. - public CieXyzToHunterLabConverter(CieXyz labWhitePoint) => this.HunterLabWhitePoint = labWhitePoint; - - /// - /// Gets the target reference white. When not set, is used. - /// - public CieXyz HunterLabWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab Convert(in CieXyz input) - { - // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab - float x = input.X, y = input.Y, z = input.Z; - float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.Z; - - float ka = ComputeKa(this.HunterLabWhitePoint); - float kb = ComputeKb(this.HunterLabWhitePoint); - - float yByYn = y / yn; - float sqrtYbyYn = MathF.Sqrt(yByYn); - float l = 100 * sqrtYbyYn; - float a = ka * (((x / xn) - yByYn) / sqrtYbyYn); - float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn); - - if (float.IsNaN(a)) - { - a = 0; - } - - if (float.IsNaN(b)) - { - b = 0; - } - - return new HunterLab(l, a, b, this.HunterLabWhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs deleted file mode 100644 index 4d15cb5d5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase -{ - private readonly Matrix4x4 conversionMatrix; - - /// - /// Initializes a new instance of the class. - /// - public CieXyzToLinearRgbConverter() - : this(Rgb.DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target working space. - public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) - { - this.TargetWorkingSpace = workingSpace; - - // Gets the inverted Rgb -> Xyz matrix - Matrix4x4.Invert(GetRgbToCieXyzMatrix(workingSpace), out Matrix4x4 inverted); - - this.conversionMatrix = inverted; - } - - /// - /// Gets the target working space. - /// - public RgbWorkingSpace TargetWorkingSpace { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb Convert(in CieXyz input) - { - var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); - - return new LinearRgb(vector, this.TargetWorkingSpace); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs deleted file mode 100644 index 07ca1c7e4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and . -/// -internal static class CmykAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Cmyk input) - { - Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K)); - return new Rgb(rgb); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Cmyk Convert(in Rgb input) - { - // To CMY - Vector3 cmy = Vector3.One - input.ToVector3(); - - // To CMYK - Vector3 k = new(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z))); - - if (MathF.Abs(k.X - 1F) < Constants.Epsilon) - { - return new Cmyk(0, 0, 0, 1F); - } - - cmy = (cmy - k) / (Vector3.One - k); - - return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs deleted file mode 100644 index 24aecc3c4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between HSL and Rgb -/// See for formulas. -/// -internal static class HslAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Hsl input) - { - float rangedH = input.H / 360F; - float r = 0; - float g = 0; - float b = 0; - float s = input.S; - float l = input.L; - - if (MathF.Abs(l) > Constants.Epsilon) - { - if (MathF.Abs(s) < Constants.Epsilon) - { - r = g = b = l; - } - else - { - float temp2 = (l < .5F) ? l * (1F + s) : l + s - (l * s); - float temp1 = (2F * l) - temp2; - - r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F); - g = GetColorComponent(temp1, temp2, rangedH); - b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F); - } - } - - return new Rgb(r, g, b); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Hsl Convert(in Rgb input) - { - float r = input.R; - float g = input.G; - float b = input.B; - - float max = MathF.Max(r, MathF.Max(g, b)); - float min = MathF.Min(r, MathF.Min(g, b)); - float chroma = max - min; - float h = 0F; - float s = 0F; - float l = (max + min) / 2F; - - if (MathF.Abs(chroma) < Constants.Epsilon) - { - return new Hsl(0F, s, l); - } - - if (MathF.Abs(r - max) < Constants.Epsilon) - { - h = (g - b) / chroma; - } - else if (MathF.Abs(g - max) < Constants.Epsilon) - { - h = 2F + ((b - r) / chroma); - } - else if (MathF.Abs(b - max) < Constants.Epsilon) - { - h = 4F + ((r - g) / chroma); - } - - h *= 60F; - if (h < 0F) - { - h += 360F; - } - - if (l <= .5F) - { - s = chroma / (max + min); - } - else - { - s = chroma / (2F - max - min); - } - - return new Hsl(h, s, l); - } - - /// - /// Gets the color component from the given values. - /// - /// The first value. - /// The second value. - /// The third value. - /// - /// The . - /// - [MethodImpl(InliningOptions.ShortMethod)] - private static float GetColorComponent(float first, float second, float third) - { - third = MoveIntoRange(third); - if (third < 0.1666667F) - { - return first + ((second - first) * 6F * third); - } - - if (third < .5F) - { - return second; - } - - if (third < 0.6666667F) - { - return first + ((second - first) * (0.6666667F - third) * 6F); - } - - return first; - } - - /// - /// Moves the specific value within the acceptable range for - /// conversion. - /// Used for converting colors to this type. - /// - /// The value to shift. - /// - /// The . - /// - [MethodImpl(InliningOptions.ShortMethod)] - private static float MoveIntoRange(float value) - { - if (value < 0F) - { - value++; - } - else if (value > 1F) - { - value--; - } - - return value; - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs deleted file mode 100644 index 527357617..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between HSV and Rgb -/// See for formulas. -/// -internal static class HsvAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Hsv input) - { - float s = input.S; - float v = input.V; - - if (MathF.Abs(s) < Constants.Epsilon) - { - return new Rgb(v, v, v); - } - - float h = (MathF.Abs(input.H - 360) < Constants.Epsilon) ? 0 : input.H / 60; - int i = (int)Math.Truncate(h); - float f = h - i; - - float p = v * (1F - s); - float q = v * (1F - (s * f)); - float t = v * (1F - (s * (1F - f))); - - float r, g, b; - switch (i) - { - case 0: - r = v; - g = t; - b = p; - break; - - case 1: - r = q; - g = v; - b = p; - break; - - case 2: - r = p; - g = v; - b = t; - break; - - case 3: - r = p; - g = q; - b = v; - break; - - case 4: - r = t; - g = p; - b = v; - break; - - default: - r = v; - g = p; - b = q; - break; - } - - return new Rgb(r, g, b); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Hsv Convert(in Rgb input) - { - float r = input.R; - float g = input.G; - float b = input.B; - - float max = MathF.Max(r, MathF.Max(g, b)); - float min = MathF.Min(r, MathF.Min(g, b)); - float chroma = max - min; - float h = 0; - float s = 0; - float v = max; - - if (MathF.Abs(chroma) < Constants.Epsilon) - { - return new Hsv(0, s, v); - } - - if (MathF.Abs(r - max) < Constants.Epsilon) - { - h = (g - b) / chroma; - } - else if (MathF.Abs(g - max) < Constants.Epsilon) - { - h = 2 + ((b - r) / chroma); - } - else if (MathF.Abs(b - max) < Constants.Epsilon) - { - h = 4 + ((r - g) / chroma); - } - - h *= 60; - if (h < 0.0) - { - h += 360; - } - - s = chroma / v; - - return new Hsv(h, s, v); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs deleted file mode 100644 index 3930e8dc2..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in HunterLab input) - { - // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab - float l = input.L, a = input.A, b = input.B; - float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; - - float ka = ComputeKa(input.WhitePoint); - float kb = ComputeKb(input.WhitePoint); - - float pow = Numerics.Pow2(l / 100F); - float sqrtPow = MathF.Sqrt(pow); - float y = pow * yn; - - float x = (((a / ka) * sqrtPow) + pow) * xn; - float z = (((b / kb) * sqrtPow) - pow) * (-zn); - - return new CieXyz(x, y, z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs deleted file mode 100644 index 27391fc80..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Provides base methods for converting between and color spaces. -/// -internal abstract class LinearRgbAndCieXyzConverterBase -{ - /// - /// Returns the correct matrix to convert between the Rgb and CieXyz color space. - /// - /// The Rgb working space. - /// The based on the chromaticity and working space. - public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) - { - DebugGuard.NotNull(workingSpace, nameof(workingSpace)); - RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; - - float xr = chromaticity.R.X; - float xg = chromaticity.G.X; - float xb = chromaticity.B.X; - float yr = chromaticity.R.Y; - float yg = chromaticity.G.Y; - float yb = chromaticity.B.Y; - - float mXr = xr / yr; - float mZr = (1 - xr - yr) / yr; - - float mXg = xg / yg; - float mZg = (1 - xg - yg) / yg; - - float mXb = xb / yb; - float mZb = (1 - xb - yb) / yb; - - Matrix4x4 xyzMatrix = new() - { - M11 = mXr, - M21 = mXg, - M31 = mXb, - M12 = 1F, - M22 = 1F, - M32 = 1F, - M13 = mZr, - M23 = mZg, - M33 = mZb, - M44 = 1F - }; - - Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); - - Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); - - // Use transposed Rows/Columns - // TODO: Is there a built in method for this multiplication? - return new Matrix4x4 - { - M11 = vector.X * mXr, - M21 = vector.Y * mXg, - M31 = vector.Z * mXb, - M12 = vector.X * 1, - M22 = vector.Y * 1, - M32 = vector.Z * 1, - M13 = vector.X * mZr, - M23 = vector.Y * mZg, - M33 = vector.Z * mZb, - M44 = 1F - }; - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs deleted file mode 100644 index 091cab993..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase -{ - private readonly Matrix4x4 conversionMatrix; - - /// - /// Initializes a new instance of the class. - /// - public LinearRgbToCieXyzConverter() - : this(Rgb.DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target working space. - public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) - { - this.SourceWorkingSpace = workingSpace; - this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); - } - - /// - /// Gets the source working space - /// - public RgbWorkingSpace SourceWorkingSpace { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz Convert(in LinearRgb input) - { - DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); - - Vector3 vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); - return new CieXyz(vector); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs deleted file mode 100644 index d41e7f74d..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and . -/// -internal static class LinearRgbToRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in LinearRgb input) => - new( - r: input.WorkingSpace.Compress(input.R), - g: input.WorkingSpace.Compress(input.G), - b: input.WorkingSpace.Compress(input.B), - workingSpace: input.WorkingSpace); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs deleted file mode 100644 index c63bbae3d..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between Rgb and LinearRgb. -/// -internal static class RgbToLinearRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static LinearRgb Convert(in Rgb input) - => new( - r: input.WorkingSpace.Expand(input.R), - g: input.WorkingSpace.Expand(input.G), - b: input.WorkingSpace.Expand(input.B), - workingSpace: input.WorkingSpace); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs deleted file mode 100644 index eda55ec4c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// See for formulas. -/// -internal static class YCbCrAndRgbConverter -{ - private static readonly Vector3 MaxBytes = new(255F); - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in YCbCr input) - { - float y = input.Y; - float cb = input.Cb - 128F; - float cr = input.Cr - 128F; - - float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); - float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); - float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); - - return new Rgb(new Vector3(r, g, b) / MaxBytes); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static YCbCr Convert(in Rgb input) - { - Vector3 rgb = input.ToVector3() * MaxBytes; - float r = rgb.X; - float g = rgb.Y; - float b = rgb.Z; - - float y = (0.299F * r) + (0.587F * g) + (0.114F * b); - float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); - float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); - - return new YCbCr(y, cb, cr); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs deleted file mode 100644 index 3b6abb041..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Chromatic adaptation. -/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] -/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD). -/// -public interface IChromaticAdaptation -{ - /// - /// Performs a linear transformation of a source color in to the destination color. - /// - /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The source color. - /// The source white point. - /// The destination white point. - /// The - CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint); - - /// - /// Performs a bulk linear transformation of a source color in to the destination color. - /// - /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The span to the source colors. - /// The span to the destination colors. - /// The source white point. - /// The destination white point. - void Transform( - ReadOnlySpan source, - Span destination, - CieXyz sourceWhitePoint, - in CieXyz destinationWhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs deleted file mode 100644 index 80bd160e8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Matrices used for transformation from to , defining the cone response domain. -/// Used in -/// -/// -/// Matrix data obtained from: -/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization -/// S. Bianco, R. Schettini -/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy -/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf -/// -public static class LmsAdaptationMatrix -{ - /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) - /// - public static readonly Matrix4x4 VonKriesHPEAdjusted - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.40024F, - M12 = 0.7076F, - M13 = -0.08081F, - M21 = -0.2263F, - M22 = 1.16532F, - M23 = 0.0457F, - M31 = 0, - M32 = 0, - M33 = 0.91822F, - M44 = 1F // Important for inverse transforms. - }); - - /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) - /// - public static readonly Matrix4x4 VonKriesHPE - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.3897F, - M12 = 0.6890F, - M13 = -0.0787F, - M21 = -0.2298F, - M22 = 1.1834F, - M23 = 0.0464F, - M31 = 0, - M32 = 0, - M33 = 1F, - M44 = 1F - }); - - /// - /// XYZ scaling chromatic adaptation transform matrix - /// - public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); - - /// - /// Bradford chromatic adaptation transform matrix (used in CMCCAT97) - /// - public static readonly Matrix4x4 Bradford - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.8951F, - M12 = 0.2664F, - M13 = -0.1614F, - M21 = -0.7502F, - M22 = 1.7135F, - M23 = 0.0367F, - M31 = 0.0389F, - M32 = -0.0685F, - M33 = 1.0296F, - M44 = 1F - }); - - /// - /// Spectral sharpening and the Bradford transform - /// - public static readonly Matrix4x4 BradfordSharp - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 1.2694F, - M12 = -0.0988F, - M13 = -0.1706F, - M21 = -0.8364F, - M22 = 1.8006F, - M23 = 0.0357F, - M31 = 0.0297F, - M32 = -0.0315F, - M33 = 1.0018F, - M44 = 1F - }); - - /// - /// CMCCAT2000 (fitted from all available color data sets) - /// - public static readonly Matrix4x4 CMCCAT2000 - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.7982F, - M12 = 0.3389F, - M13 = -0.1371F, - M21 = -0.5918F, - M22 = 1.5512F, - M23 = 0.0406F, - M31 = 0.0008F, - M32 = 0.239F, - M33 = 0.9753F, - M44 = 1F - }); - - /// - /// CAT02 (optimized for minimizing CIELAB differences) - /// - public static readonly Matrix4x4 CAT02 - = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.7328F, - M12 = 0.4296F, - M13 = -0.1624F, - M21 = -0.7036F, - M22 = 1.6975F, - M23 = 0.0061F, - M31 = 0.0030F, - M32 = 0.0136F, - M33 = 0.9834F, - M44 = 1F - }); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs deleted file mode 100644 index 625e6c551..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Represents the chromaticity coordinates of RGB primaries. -/// One of the specifiers of . -/// -public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// The chromaticity coordinates of the red channel. - /// The chromaticity coordinates of the green channel. - /// The chromaticity coordinates of the blue channel. - public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) - { - this.R = r; - this.G = g; - this.B = b; - } - - /// - /// Gets the chromaticity coordinates of the red channel. - /// - public CieXyChromaticityCoordinates R { get; } - - /// - /// Gets the chromaticity coordinates of the green channel. - /// - public CieXyChromaticityCoordinates G { get; } - - /// - /// Gets the chromaticity coordinates of the blue channel. - /// - public CieXyChromaticityCoordinates B { get; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for inequality - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) - { - return !left.Equals(right); - } - - /// - public override bool Equals(object? obj) - { - return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other); - } - - /// - public bool Equals(RgbPrimariesChromaticityCoordinates other) - { - return this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); - } - - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs deleted file mode 100644 index 8f33e59f5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Implementation of the Von Kries chromatic adaptation model. -/// -/// -/// Transformation described here: -/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html -/// -public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation -{ - private readonly CieXyzAndLmsConverter converter; - - /// - /// Initializes a new instance of the class. - /// - public VonKriesChromaticAdaptation() - : this(new CieXyzAndLmsConverter()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The transformation matrix used for the conversion (definition of the cone response domain). - /// - /// - public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) - : this(new CieXyzAndLmsConverter(transformationMatrix)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color converter - internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) => this.converter = converter; - - /// - public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) - { - if (sourceWhitePoint.Equals(destinationWhitePoint)) - { - return source; - } - - Lms sourceColorLms = this.converter.Convert(source); - Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); - Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); - - Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); - var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); - - return this.converter.Convert(targetColorLms); - } - - /// - public void Transform( - ReadOnlySpan source, - Span destination, - CieXyz sourceWhitePoint, - in CieXyz destinationWhitePoint) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - if (sourceWhitePoint.Equals(destinationWhitePoint)) - { - source.CopyTo(destination[..count]); - return; - } - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - - Lms sourceColorLms = this.converter.Convert(sp); - Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); - Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); - - Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); - var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); - - dp = this.converter.Convert(targetColorLms); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs deleted file mode 100644 index e95f1f2b4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The gamma working space. -/// -public sealed class GammaWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The gamma value. - /// The reference white point. - /// The chromaticity of the rgb primaries. - public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; - - /// - /// Gets the gamma value. - /// - public float Gamma { get; } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => GammaCompanding.Compress(channel, this.Gamma); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => GammaCompanding.Expand(channel, this.Gamma); - - /// - public override bool Equals(object? obj) - { - if (obj is null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is GammaWorkingSpace other) - { - return this.Gamma.Equals(other.Gamma) - && this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - return false; - } - - /// - public override int GetHashCode() => HashCode.Combine( - this.WhitePoint, - this.ChromaticityCoordinates, - this.Gamma); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs deleted file mode 100644 index 199f6c8d8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// L* working space. -/// -public sealed class LWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => LCompanding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => LCompanding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs deleted file mode 100644 index 52cc0f95a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. -/// -public sealed class Rec2020WorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => Rec2020Companding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => Rec2020Companding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs deleted file mode 100644 index c030e9102..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Rec. 709 (ITU-R Recommendation BT.709) working space. -/// -public sealed class Rec709WorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => Rec709Companding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => Rec709Companding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs deleted file mode 100644 index dd1dc62a8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Base class for all implementations of . -/// -public abstract class RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - /// Gets the reference white point - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the chromaticity of the rgb primaries. - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// - /// For more info see: - /// - /// - /// The channel value. - /// The representing the linear channel value. - public abstract float Expand(float channel); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). - /// - /// - /// For more info see: - /// - /// - /// The channel value. - /// The representing the nonlinear channel value. - public abstract float Compress(float channel); - - /// - public override bool Equals(object? obj) - { - if (obj is null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is RgbWorkingSpace other) - { - return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - return false; - } - - /// - public override int GetHashCode() - => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs deleted file mode 100644 index 767157f4c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The sRgb working space. -/// -public sealed class SRgbWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => SRgbCompanding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => SRgbCompanding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs deleted file mode 100644 index cf18c70c7..000000000 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a Hsl (hue, saturation, lightness) color. -/// -public readonly struct Hsl : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(360, 1, 1); - - /// - /// Initializes a new instance of the struct. - /// - /// The h hue component. - /// The s saturation component. - /// The l value (lightness) component. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsl(float h, float s, float l) - : this(new Vector3(h, s, l)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the h, s, l components. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsl(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.H = vector.X; - this.S = vector.Y; - this.L = vector.Z; - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public readonly float H { get; } - - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public readonly float S { get; } - - /// - /// Gets the lightness component. - /// A value ranging between 0 and 1. - /// - public readonly float L { get; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Hsl left, Hsl right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L); - - /// - public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Hsl other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Hsl other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.L.Equals(other.L); -} diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs deleted file mode 100644 index 87c16c0b6..000000000 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). -/// -public readonly struct Hsv : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(360, 1, 1); - - /// - /// Initializes a new instance of the struct. - /// - /// The h hue component. - /// The s saturation component. - /// The v value (brightness) component. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsv(float h, float s, float v) - : this(new Vector3(h, s, v)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the h, s, v components. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsv(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.H = vector.X; - this.S = vector.Y; - this.V = vector.Z; - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public readonly float H { get; } - - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public readonly float S { get; } - - /// - /// Gets the value (brightness) component. - /// A value ranging between 0 and 1. - /// - public readonly float V { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); - - /// - public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Hsv other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.V.Equals(other.V); -} diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs deleted file mode 100644 index 516574b09..000000000 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an Hunter LAB color. -/// . -/// -public readonly struct HunterLab : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.C; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(float l, float a, float b) - : this(new Vector3(l, a, b), DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(float l, float a, float b, CieXyz whitePoint) - : this(new Vector3(l, a, b), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l a b components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(Vector3 vector, CieXyz whitePoint) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.A = vector.Y; - this.B = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a color component. - /// A value usually ranging from -100 to 100. Negative is green, positive magenta. - /// - public readonly float A { get; } - - /// - /// Gets the b color component. - /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow - /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color. - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is HunterLab other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(HunterLab other) - => this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs deleted file mode 100644 index 7c25305c2..000000000 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// The well known standard illuminants. -/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting -/// -/// -/// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html -///
    -/// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant -///
    -public static class Illuminants -{ - /// - /// Incandescent / Tungsten - /// - public static readonly CieXyz A = new CieXyz(1.09850F, 1F, 0.35585F); - - /// - /// Direct sunlight at noon (obsoleteF) - /// - public static readonly CieXyz B = new CieXyz(0.99072F, 1F, 0.85223F); - - /// - /// Average / North sky Daylight (obsoleteF) - /// - public static readonly CieXyz C = new CieXyz(0.98074F, 1F, 1.18232F); - - /// - /// Horizon Light. ICC profile PCS - /// - public static readonly CieXyz D50 = new CieXyz(0.96422F, 1F, 0.82521F); - - /// - /// Mid-morning / Mid-afternoon Daylight - /// - public static readonly CieXyz D55 = new CieXyz(0.95682F, 1F, 0.92149F); - - /// - /// Noon Daylight: TelevisionF, sRGB color space - /// - public static readonly CieXyz D65 = new CieXyz(0.95047F, 1F, 1.08883F); - - /// - /// North sky Daylight - /// - public static readonly CieXyz D75 = new CieXyz(0.94972F, 1F, 1.22638F); - - /// - /// Equal energy - /// - public static readonly CieXyz E = new CieXyz(1F, 1F, 1F); - - /// - /// Cool White Fluorescent - /// - public static readonly CieXyz F2 = new CieXyz(0.99186F, 1F, 0.67393F); - - /// - /// D65 simulatorF, Daylight simulator - /// - public static readonly CieXyz F7 = new CieXyz(0.95041F, 1F, 1.08747F); - - /// - /// Philips TL84F, Ultralume 40 - /// - public static readonly CieXyz F11 = new CieXyz(1.00962F, 1F, 0.64350F); -} diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs deleted file mode 100644 index 49c7814a0..000000000 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an linear Rgb color with specified working space -/// -public readonly struct LinearRgb : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - - /// - /// The default LinearRgb working space. - /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(float r, float g, float b) - : this(r, g, b, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) - : this(new Vector3(r, g, b), workingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(Vector3 vector) - : this(vector, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - /// The LinearRgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) - { - // Clamp to 0-1 range. - vector = Vector3.Clamp(vector, Min, Max); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.WorkingSpace = workingSpace; - } - - /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public readonly float R { get; } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public readonly float G { get; } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public readonly float B { get; } - - /// - /// Gets the LinearRgb color space - /// - public readonly RgbWorkingSpace WorkingSpace { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(LinearRgb left, LinearRgb right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(LinearRgb left, LinearRgb right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.R, this.G, this.B); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is LinearRgb other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(LinearRgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); -} diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs deleted file mode 100644 index 99210ff6e..000000000 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// LMS is a color space represented by the response of the three types of cones of the human eye, -/// named after their responsivity (sensitivity) at long, medium and short wavelengths. -/// -/// -public readonly struct Lms : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// L represents the responsivity at long wavelengths. - /// M represents the responsivity at medium wavelengths. - /// S represents the responsivity at short wavelengths. - [MethodImpl(InliningOptions.ShortMethod)] - public Lms(float l, float m, float s) - : this(new Vector3(l, m, s)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, m, s components. - [MethodImpl(InliningOptions.ShortMethod)] - public Lms(Vector3 vector) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.M = vector.Y; - this.S = vector.Z; - } - - /// - /// Gets the L long component. - /// A value usually ranging between -1 and 1. - /// - public readonly float L { get; } - - /// - /// Gets the M medium component. - /// A value usually ranging between -1 and 1. - /// - public readonly float M { get; } - - /// - /// Gets the S short component. - /// A value usually ranging between -1 and 1. - /// - public readonly float S { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Lms left, Lms right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Lms left, Lms right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.L, this.M, this.S); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); - - /// - public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Lms other) - => this.L.Equals(other.L) - && this.M.Equals(other.M) - && this.S.Equals(other.S); -} diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs deleted file mode 100644 index 55052a710..000000000 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an RGB color with specified working space. -/// -public readonly struct Rgb : IEquatable -{ - /// - /// The default rgb working space. - /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; - - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(float r, float g, float b) - : this(r, g, b, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) - : this(new Vector3(r, g, b), workingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(Vector3 vector) - : this(vector, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) - { - vector = Vector3.Clamp(vector, Min, Max); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.WorkingSpace = workingSpace; - } - - /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public readonly float R { get; } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public readonly float G { get; } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public readonly float B { get; } - - /// - /// Gets the Rgb color space - /// - public readonly RgbWorkingSpace WorkingSpace { get; } - - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// The instance of to convert. - /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb(Rgb24 color) => new(color.R / 255F, color.G / 255F, color.B / 255F); - - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// The instance of to convert. - /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb(Rgba32 color) => new(color.R / 255F, color.G / 255F, color.B / 255F); - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Rgb left, Rgb right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.R, this.G, this.B); - - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Rgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); -} diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs deleted file mode 100644 index 53c8c2cf0..000000000 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces.Companding; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Chromaticity coordinates based on: -/// -public static class RgbWorkingSpaces -{ - /// - /// sRgb working space. - /// - /// - /// Uses proper companding function, according to: - /// - /// - public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Simplified sRgb working space (uses gamma companding instead of ). - /// See also . - /// - public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Rec. 709 (ITU-R Recommendation BT.709) working space. - /// - public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); - - /// - /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. - /// - public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); - - /// - /// ECI Rgb v2 working space. - /// - public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); - - /// - /// Adobe Rgb (1998) working space. - /// - public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Apple sRgb working space. - /// - public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); - - /// - /// Best Rgb working space. - /// - public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); - - /// - /// Beta Rgb working space. - /// - public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); - - /// - /// Bruce Rgb working space. - /// - public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// CIE Rgb working space. - /// - public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); - - /// - /// ColorMatch Rgb working space. - /// - public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); - - /// - /// Don Rgb 4 working space. - /// - public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); - - /// - /// Ekta Space PS5 working space. - /// - public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); - - /// - /// NTSC Rgb working space. - /// - public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); - - /// - /// PAL/SECAM Rgb working space. - /// - public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// ProPhoto Rgb working space. - /// - public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); - - /// - /// SMPTE-C Rgb working space. - /// - public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); - - /// - /// Wide Gamut Rgb working space. - /// - public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); -} diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs deleted file mode 100644 index acb59388f..000000000 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. -/// -/// -/// -public readonly struct YCbCr : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(255); - - /// - /// Initializes a new instance of the struct. - /// - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - [MethodImpl(InliningOptions.ShortMethod)] - public YCbCr(float y, float cb, float cr) - : this(new Vector3(y, cb, cr)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the y, cb, cr components. - [MethodImpl(InliningOptions.ShortMethod)] - public YCbCr(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.Y = vector.X; - this.Cb = vector.Y; - this.Cr = vector.Z; - } - - /// - /// Gets the Y luminance component. - /// A value ranging between 0 and 255. - /// - public readonly float Y { get; } - - /// - /// Gets the Cb chroma component. - /// A value ranging between 0 and 255. - /// - public readonly float Cb { get; } - - /// - /// Gets the Cr chroma component. - /// A value ranging between 0 and 255. - /// - public readonly float Cr { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); - - /// - public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})"); - - /// - public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(YCbCr other) - => this.Y.Equals(other.Y) - && this.Cb.Equals(other.Cb) - && this.Cr.Equals(other.Cr); -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs deleted file mode 100644 index 52cb63d21..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLabTests -{ - [Fact] - public void CieLabConstructorAssignsFields() - { - const float l = 75F; - const float a = -64F; - const float b = 87F; - var cieLab = new CieLab(l, a, b); - - Assert.Equal(l, cieLab.L); - Assert.Equal(a, cieLab.A); - Assert.Equal(b, cieLab.B); - } - - [Fact] - public void CieLabEquality() - { - var x = default(CieLab); - var y = new CieLab(Vector3.One); - - Assert.True(default(CieLab) == default(CieLab)); - Assert.True(new CieLab(1, 0, 1) != default(CieLab)); - Assert.False(new CieLab(1, 0, 1) == default(CieLab)); - Assert.Equal(default(CieLab), default(CieLab)); - Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); - Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(new CieLab(1, 0, 1) == default(CieLab)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs deleted file mode 100644 index 83e4d8a59..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLchTests -{ - [Fact] - public void CieLchConstructorAssignsFields() - { - const float l = 75F; - const float c = 64F; - const float h = 287F; - var cieLch = new CieLch(l, c, h); - - Assert.Equal(l, cieLch.L); - Assert.Equal(c, cieLch.C); - Assert.Equal(h, cieLch.H); - } - - [Fact] - public void CieLchEquality() - { - var x = default(CieLch); - var y = new CieLch(Vector3.One); - - Assert.True(default(CieLch) == default(CieLch)); - Assert.False(default(CieLch) != default(CieLch)); - Assert.Equal(default(CieLch), default(CieLch)); - Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1)); - Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs deleted file mode 100644 index f6c68aae9..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLchuvTests -{ - [Fact] - public void CieLchuvConstructorAssignsFields() - { - const float l = 75F; - const float c = 64F; - const float h = 287F; - var cieLchuv = new CieLchuv(l, c, h); - - Assert.Equal(l, cieLchuv.L); - Assert.Equal(c, cieLchuv.C); - Assert.Equal(h, cieLchuv.H); - } - - [Fact] - public void CieLchuvEquality() - { - var x = default(CieLchuv); - var y = new CieLchuv(Vector3.One); - - Assert.True(default(CieLchuv) == default(CieLchuv)); - Assert.False(default(CieLchuv) != default(CieLchuv)); - Assert.Equal(default(CieLchuv), default(CieLchuv)); - Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1)); - Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs deleted file mode 100644 index 0ebf1bdea..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLuvTests -{ - [Fact] - public void CieLuvConstructorAssignsFields() - { - const float l = 75F; - const float c = -64F; - const float h = 87F; - var cieLuv = new CieLuv(l, c, h); - - Assert.Equal(l, cieLuv.L); - Assert.Equal(c, cieLuv.U); - Assert.Equal(h, cieLuv.V); - } - - [Fact] - public void CieLuvEquality() - { - var x = default(CieLuv); - var y = new CieLuv(Vector3.One); - - Assert.True(default(CieLuv) == default(CieLuv)); - Assert.False(default(CieLuv) != default(CieLuv)); - Assert.Equal(default(CieLuv), default(CieLuv)); - Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1)); - Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs deleted file mode 100644 index 061d6c432..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyChromaticityCoordinatesTests -{ - [Fact] - public void CieXyChromaticityCoordinatesConstructorAssignsFields() - { - const float x = .75F; - const float y = .64F; - var coordinates = new CieXyChromaticityCoordinates(x, y); - - Assert.Equal(x, coordinates.X); - Assert.Equal(y, coordinates.Y); - } - - [Fact] - public void CieXyChromaticityCoordinatesEquality() - { - var x = default(CieXyChromaticityCoordinates); - var y = new CieXyChromaticityCoordinates(1, 1); - - Assert.True(default(CieXyChromaticityCoordinates) == default(CieXyChromaticityCoordinates)); - Assert.True(new CieXyChromaticityCoordinates(1, 0) != default(CieXyChromaticityCoordinates)); - Assert.False(new CieXyChromaticityCoordinates(1, 0) == default(CieXyChromaticityCoordinates)); - Assert.Equal(default(CieXyChromaticityCoordinates), default(CieXyChromaticityCoordinates)); - Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0)); - Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1)); - Assert.False(x.Equals(y)); - Assert.False(new CieXyChromaticityCoordinates(1, 0) == default(CieXyChromaticityCoordinates)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs deleted file mode 100644 index f1eb12640..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyyTests -{ - [Fact] - public void CieXyyConstructorAssignsFields() - { - const float x = 75F; - const float y = 64F; - const float yl = 287F; - var cieXyy = new CieXyy(x, y, yl); - - Assert.Equal(x, cieXyy.X); - Assert.Equal(y, cieXyy.Y); - Assert.Equal(y, cieXyy.Y); - } - - [Fact] - public void CieXyyEquality() - { - var x = default(CieXyy); - var y = new CieXyy(Vector3.One); - - Assert.True(default(CieXyy) == default(CieXyy)); - Assert.False(default(CieXyy) != default(CieXyy)); - Assert.Equal(default(CieXyy), default(CieXyy)); - Assert.Equal(new CieXyy(1, 0, 1), new CieXyy(1, 0, 1)); - Assert.Equal(new CieXyy(Vector3.One), new CieXyy(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs deleted file mode 100644 index 6de961cf1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyzTests -{ - [Fact] - public void CieXyzConstructorAssignsFields() - { - const float x = 75F; - const float y = 64F; - const float z = 287F; - var cieXyz = new CieXyz(x, y, z); - - Assert.Equal(x, cieXyz.X); - Assert.Equal(y, cieXyz.Y); - Assert.Equal(z, cieXyz.Z); - } - - [Fact] - public void CieXyzEquality() - { - var x = default(CieXyz); - var y = new CieXyz(Vector3.One); - - Assert.True(default(CieXyz) == default(CieXyz)); - Assert.False(default(CieXyz) != default(CieXyz)); - Assert.Equal(default(CieXyz), default(CieXyz)); - Assert.Equal(new CieXyz(1, 0, 1), new CieXyz(1, 0, 1)); - Assert.Equal(new CieXyz(Vector3.One), new CieXyz(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs b/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs deleted file mode 100644 index b4e55ed24..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CmykTests -{ - [Fact] - public void CmykConstructorAssignsFields() - { - const float c = .75F; - const float m = .64F; - const float y = .87F; - const float k = .334F; - var cmyk = new Cmyk(c, m, y, k); - - Assert.Equal(c, cmyk.C); - Assert.Equal(m, cmyk.M); - Assert.Equal(y, cmyk.Y); - Assert.Equal(k, cmyk.K); - } - - [Fact] - public void CmykEquality() - { - var x = default(Cmyk); - var y = new Cmyk(Vector4.One); - - Assert.True(default(Cmyk) == default(Cmyk)); - Assert.False(default(Cmyk) != default(Cmyk)); - Assert.Equal(default(Cmyk), default(Cmyk)); - Assert.Equal(new Cmyk(1, 0, 1, 0), new Cmyk(1, 0, 1, 0)); - Assert.Equal(new Cmyk(Vector4.One), new Cmyk(Vector4.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs deleted file mode 100644 index 3fb359890..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding; - -/// -/// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. -/// -public class CompandingTests -{ - private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(.00001F); - - [Fact] - public void Rec2020Companding_IsCorrect() - { - const float input = .667F; - float e = Rec2020Companding.Expand(input); - float c = Rec2020Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4484759F, input); - } - - [Fact] - public void Rec709Companding_IsCorrect() - { - const float input = .667F; - float e = Rec709Companding.Expand(input); - float c = Rec709Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4483577F, input); - } - - [Fact] - public void SRgbCompanding_IsCorrect() - { - const float input = .667F; - float e = SRgbCompanding.Expand(input); - float c = SRgbCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .40242353F, input); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - public void SRgbCompanding_Expand_VectorSpan(int length) - { - var rnd = new Random(42); - Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - var expected = new Vector4[source.Length]; - - for (int i = 0; i < source.Length; i++) - { - Vector4 s = source[i]; - ref Vector4 e = ref expected[i]; - SRgbCompanding.Expand(ref s); - e = s; - } - - SRgbCompanding.Expand(source); - - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - public void SRgbCompanding_Compress_VectorSpan(int length) - { - var rnd = new Random(42); - Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - var expected = new Vector4[source.Length]; - - for (int i = 0; i < source.Length; i++) - { - Vector4 s = source[i]; - ref Vector4 e = ref expected[i]; - SRgbCompanding.Compress(ref s); - e = s; - } - - SRgbCompanding.Compress(source); - - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); - } - - [Fact] - public void GammaCompanding_IsCorrect() - { - const float gamma = 2.2F; - const float input = .667F; - float e = GammaCompanding.Expand(input, gamma); - float c = GammaCompanding.Compress(e, gamma); - CompandingIsCorrectImpl(e, c, .41027668F, input); - } - - [Fact] - public void LCompanding_IsCorrect() - { - const float input = .667F; - float e = LCompanding.Expand(input); - float c = LCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .36236193F, input); - } - - private static void CompandingIsCorrectImpl(float e, float c, float expanded, float compressed) - { - Assert.Equal(expanded, e, FloatComparer); - Assert.Equal(compressed, c, FloatComparer); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs deleted file mode 100644 index d66a73b5a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Allows the approximate comparison of colorspace component values. -/// -internal readonly struct ApproximateColorSpaceComparer : - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer -{ - private readonly float epsilon; - - /// - /// Initializes a new instance of the class. - /// - /// The comparison error difference epsilon to use. - public ApproximateColorSpaceComparer(float epsilon = 1F) => this.epsilon = epsilon; - - /// - public bool Equals(Rgb x, Rgb y) - { - return this.Equals(x.R, y.R) - && this.Equals(x.G, y.G) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(Rgb obj) => obj.GetHashCode(); - - /// - public bool Equals(LinearRgb x, LinearRgb y) - { - return this.Equals(x.R, y.R) - && this.Equals(x.G, y.G) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(LinearRgb obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLab x, CieLab y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.A, y.A) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(CieLab obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLch x, CieLch y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.C, y.C) - && this.Equals(x.H, y.H); - } - - /// - public int GetHashCode(CieLch obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLchuv x, CieLchuv y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.C, y.C) - && this.Equals(x.H, y.H); - } - - /// - public int GetHashCode(CieLchuv obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLuv x, CieLuv y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.U, y.U) - && this.Equals(x.V, y.V); - } - - /// - public int GetHashCode(CieLuv obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyz x, CieXyz y) - { - return this.Equals(x.X, y.X) - && this.Equals(x.Y, y.Y) - && this.Equals(x.Z, y.Z); - } - - /// - public int GetHashCode(CieXyz obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyy x, CieXyy y) - { - return this.Equals(x.X, y.X) - && this.Equals(x.Y, y.Y) - && this.Equals(x.Yl, y.Yl); - } - - /// - public int GetHashCode(CieXyy obj) => obj.GetHashCode(); - - /// - public bool Equals(Cmyk x, Cmyk y) - { - return this.Equals(x.C, y.C) - && this.Equals(x.M, y.M) - && this.Equals(x.Y, y.Y) - && this.Equals(x.K, y.K); - } - - /// - public int GetHashCode(Cmyk obj) => obj.GetHashCode(); - - /// - public bool Equals(HunterLab x, HunterLab y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.A, y.A) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(HunterLab obj) => obj.GetHashCode(); - - /// - public bool Equals(Hsl x, Hsl y) - { - return this.Equals(x.H, y.H) - && this.Equals(x.S, y.S) - && this.Equals(x.L, y.L); - } - - /// - public int GetHashCode(Hsl obj) => obj.GetHashCode(); - - /// - public bool Equals(Hsv x, Hsv y) - { - return this.Equals(x.H, y.H) - && this.Equals(x.S, y.S) - && this.Equals(x.V, y.V); - } - - /// - public int GetHashCode(Hsv obj) => obj.GetHashCode(); - - /// - public bool Equals(Lms x, Lms y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.M, y.M) - && this.Equals(x.S, y.S); - } - - /// - public int GetHashCode(Lms obj) => obj.GetHashCode(); - - /// - public bool Equals(YCbCr x, YCbCr y) - { - return this.Equals(x.Y, y.Y) - && this.Equals(x.Cb, y.Cb) - && this.Equals(x.Cr, y.Cr); - } - - /// - public int GetHashCode(YCbCr obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyChromaticityCoordinates x, CieXyChromaticityCoordinates y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); - - /// - public int GetHashCode(CieXyChromaticityCoordinates obj) => obj.GetHashCode(); - - /// - public bool Equals(RgbPrimariesChromaticityCoordinates x, RgbPrimariesChromaticityCoordinates y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); - - /// - public int GetHashCode(RgbPrimariesChromaticityCoordinates obj) => obj.GetHashCode(); - - /// - public bool Equals(GammaWorkingSpace x, GammaWorkingSpace y) - { - if (x is GammaWorkingSpace g1 && y is GammaWorkingSpace g2) - { - return this.Equals(g1.Gamma, g2.Gamma) - && this.Equals(g1.WhitePoint, g2.WhitePoint) - && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); - } - - return false; - } - - /// - public int GetHashCode(GammaWorkingSpace obj) => obj.GetHashCode(); - - /// - public bool Equals(RgbWorkingSpace x, RgbWorkingSpace y) - { - return this.Equals(x.WhitePoint, y.WhitePoint) - && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); - } - - /// - public int GetHashCode(RgbWorkingSpace obj) => obj.GetHashCode(); - - private bool Equals(float x, float y) - { - float d = x - y; - return d >= -this.epsilon && d <= this.epsilon; - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs deleted file mode 100644 index d71f0fa5e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, 50, 180, 100, -50, 0)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] - [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] - [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] - public void Convert_Lch_to_Lab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, -50, 0, 100, 50, 180)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] - [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] - [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] - public void Convert_Lab_to_Lch(float l, float a, float b, float l2, float c, float h) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLch(l2, c, h); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs deleted file mode 100644 index d8378217e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLchuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] - public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 30.66194, 200, 352.7564)] - public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLchuv(l2, c, h); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs deleted file mode 100644 index 220ef4f22..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(10, 36.0555, 303.6901, 10.0151367, -23.9644356, 17.0226)] - public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] - public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs deleted file mode 100644 index 49ea4502f..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.8644734, 0.06098868, 0.06509002, 36.05552, 275.6228, 10.01517)] - public void Convert_CieXyy_to_CieLab(float x, float y, float yl, float l, float a, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLab(l, a, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.8644734, 0.06098868, 0.06509002)] - public void Convert_CieLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs deleted file mode 100644 index 7f9470e20..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndCmykConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 1, 0, 0, 0)] - [InlineData(0, 1, 0.6156551, 5.960464E-08, 55.063, 82.54871, 23.16506)] - public void Convert_Cmyk_to_CieLab(float c, float m, float y, float k, float l, float a, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(36.0555, 303.6901, 10.01514, 0, 1, 0.6156551, 5.960464E-08)] - public void Convert_CieLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs deleted file mode 100644 index 1af6a9315..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(336.9393, 1, 0.5, 55.063, 82.54868, 23.16508)] - public void Convert_Hsl_to_CieLab(float h, float s, float ll, float l, float a, float b) - { - // Arrange - var input = new Hsl(h, s, ll); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.5)] - public void Convert_CieLab_to_Hsl(float l, float a, float b, float h, float s, float ll) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Hsl(h, s, ll); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs deleted file mode 100644 index c7c3ad19b..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(336.9393, 1, 0.9999999, 55.063, 82.54871, 23.16504)] - public void Convert_Hsv_to_CieLab(float h, float s, float v, float l, float a, float b) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.9999999)] - public void Convert_CieLab_to_Hsv(float l, float a, float b, float h, float s, float v) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs deleted file mode 100644 index 81fff6e14..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(27.51646, 556.9392, -0.03974226, 36.05554, 275.6227, 10.01519)] - public void Convert_HunterLab_to_CieLab(float l2, float a2, float b2, float l, float a, float b) - { - // Arrange - var input = new HunterLab(l2, a2, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 27.51646, 556.9392, -0.03974226)] - public void Convert_CieLab_to_HunterLab(float l, float a, float b, float l2, float a2, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new HunterLab(l2, a2, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs deleted file mode 100644 index 68547d8a2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0.1221596, 55.063, 82.54871, 23.16505)] - public void Convert_LinearRgb_to_CieLab(float r, float g, float b2, float l, float a, float b) - { - // Arrange - var input = new LinearRgb(r, g, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 1, 0, 0.1221596)] - public void Convert_CieLab_to_LinearRgb(float l, float a, float b, float r, float g, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new LinearRgb(r, g, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs deleted file mode 100644 index 2918b6f62..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.8303261, -0.5776886, 0.1133359, 36.05553, 275.6228, 10.01518)] - public void Convert_Lms_to_CieLab(float l2, float m, float s, float l, float a, float b) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.8303261, -0.5776886, 0.1133359)] - public void Convert_CieLab_to_Lms(float l, float a, float b, float l2, float m, float s) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs deleted file mode 100644 index 3acf695d1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.9999999, 0, 0.384345, 55.063, 82.54871, 23.16505)] - public void Convert_Rgb_to_CieLab(float r, float g, float b2, float l, float a, float b) - { - // Arrange - var input = new Rgb(r, g, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.9999999, 0, 0.384345)] - public void Convert_CieLab_to_Rgb(float l, float a, float b, float r, float g, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Rgb(r, g, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs deleted file mode 100644 index f13e989a8..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(87.4179, 133.9763, 247.5308, 55.06287, 82.54838, 23.1697)] - public void Convert_YCbCr_to_CieLab(float y, float cb, float cr, float l, float a, float b) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLab(l, a, b); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 303.6901, 10.01514, 87.4179, 133.9763, 247.5308)] - public void Convert_CieLab_to_YCbCr(float l, float a, float b, float y, float cb, float cr) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs deleted file mode 100644 index b50d47aeb..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 34.89777, 187.6642, -7.181467)] - public void Convert_CieLch_to_CieLuv(float l, float c, float h, float l2, float u, float v) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(34.89777, 187.6642, -7.181467, 36.05552, 103.6901, 10.01514)] - public void Convert_CieLuv_to_CieLch(float l2, float u, float v, float l, float c, float h) - { - // Arrange - var input = new CieLuv(l2, u, v); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs deleted file mode 100644 index 93f082886..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.6529307, 0.2147411, 0.08447381)] - public void Convert_CieLch_to_CieXyy(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.6529307, 0.2147411, 0.08447381, 36.05552, 103.6901, 10.01515)] - public void Convert_CieXyy_to_CieLch(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs deleted file mode 100644 index 0bc76562b..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.4207301)] - public void Convert_CieLch_to_Hsl(float l, float c, float h, float h2, float s, float l2) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Hsl(h2, s, l2); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(341.959, 1, 0.4207301, 46.13444, 78.0637, 22.90503)] - public void Convert_Hsl_to_CieLch(float h2, float s, float l2, float l, float c, float h) - { - // Arrange - var input = new Hsl(h2, s, l2); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs deleted file mode 100644 index bb28c0394..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.8414602)] - public void Convert_CieLch_to_Hsv(float l, float c, float h, float h2, float s, float v) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Hsv(h2, s, v); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(341.959, 1, 0.8414602, 46.13444, 78.0637, 22.90501)] - public void Convert_Hsv_to_CieLch(float h2, float s, float v, float l, float c, float h) - { - // Arrange - var input = new Hsv(h2, s, v); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs deleted file mode 100644 index 9892ac8f5..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 29.41358, 106.6302, 9.102425)] - public void Convert_CieLch_to_HunterLab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new HunterLab(l2, a, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(29.41358, 106.6302, 9.102425, 36.05551, 103.6901, 10.01515)] - public void Convert_HunterLab_to_CieLch(float l2, float a, float b, float l, float c, float h) - { - // Arrange - var input = new HunterLab(l2, a, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs deleted file mode 100644 index 3ecfadf11..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.6765013, 0, 0.05209038)] - public void Convert_CieLch_to_LinearRgb(float l, float c, float h, float r, float g, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.6765013, 0, 0.05209038, 46.13445, 78.06367, 22.90504)] - public void Convert_LinearRgb_to_CieLch(float r, float g, float b, float l, float c, float h) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs deleted file mode 100644 index 7a1939121..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.2440057, -0.04603009, 0.05780027)] - public void Convert_CieLch_to_Lms(float l, float c, float h, float l2, float m, float s) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.2440057, -0.04603009, 0.05780027, 36.05552, 103.6901, 10.01515)] - public void Convert_Lms_to_CieLch(float l2, float m, float s, float l, float c, float h) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs deleted file mode 100644 index 013e79534..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.8414602, 0, 0.2530123)] - public void Convert_CieLch_to_Rgb(float l, float c, float h, float r, float g, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.8414602, 0, 0.2530123, 46.13444, 78.0637, 22.90503)] - public void Convert_Rgb_to_CieLch(float r, float g, float b, float l, float c, float h) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs deleted file mode 100644 index bcde97102..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 103.6901, 10.01514, 71.5122, 124.053, 230.0401)] - public void Convert_CieLch_to_YCbCr(float l, float c, float h, float y, float cb, float cr) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(71.5122, 124.053, 230.0401, 46.23178, 78.1114, 22.7662)] - public void Convert_YCbCr_to_CieLch(float y, float cb, float cr, float l, float c, float h) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLch(l, c, h); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs deleted file mode 100644 index 1098de91e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchuvAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.73742, 64.79149, 30.1786, 36.0555, 103.6901, 10.01513)] - public void Convert_CieLch_to_CieLchuv(float l2, float c2, float h2, float l, float c, float h) - { - // Arrange - var input = new CieLch(l2, c2, h2); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(36.0555, 103.6901, 10.01514, 36.73742, 64.79149, 30.1786)] - public void Convert_CieLchuv_to_CieLch(float l, float c, float h, float l2, float c2, float h2) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLch(l2, c2, h2); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs deleted file mode 100644 index ba3bc9e79..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLchuvAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, 50, 180, 100, -50, 0)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] - [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] - [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] - public void Convert_CieLchuv_to_CieLuv(float l, float c, float h, float l2, float u, float v) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, -50, 0, 100, 50, 180)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] - [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] - [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] - [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] - public void Convert_CieLuv_to_CieLchuv(float l, float u, float v, float l2, float c, float h) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieLchuv(l2, c, h); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs deleted file mode 100644 index 176a0e217..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchuvAndCmykConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 1, 0, 0, 0)] - [InlineData(0, 0.8576171, 0.7693201, 0.3440427, 36.0555, 103.6901, 10.01514)] - public void Convert_Cmyk_to_CieLchuv(float c2, float m, float y, float k, float l, float c, float h) - { - // Arrange - var input = new Cmyk(c2, m, y, k); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(36.0555, 103.6901, 10.01514, 0, 0.8576171, 0.7693201, 0.3440427)] - public void Convert_CieLchuv_to_Cmyk(float l, float c, float h, float c2, float m, float y, float k) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new Cmyk(c2, m, y, k); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs deleted file mode 100644 index e32cc6ebf..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.5646762, 0.2932749, 0.09037033)] - public void Convert_CieLuv_to_CieXyy(float l, float u, float v, float x, float y, float yl) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5646762, 0.2932749, 0.09037033, 36.0555, 103.6901, 10.01514)] - public void Convert_CieXyy_to_CieLuv(float x, float y, float yl, float l, float u, float v) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs deleted file mode 100644 index 95f07465a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.7115612, 0.3765343)] - public void Convert_CieLuv_to_Hsl(float l, float u, float v, float h, float s, float l2) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Hsl(h, s, l2); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(347.3767, 0.7115612, 0.3765343, 36.0555, 93.69012, 10.01514)] - public void Convert_Hsl_to_CieLuv(float h, float s, float l2, float l, float u, float v) - { - // Arrange - var input = new Hsl(h, s, l2); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs deleted file mode 100644 index ddb90f0ef..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.8314762, 0.6444615)] - public void Convert_CieLuv_to_Hsv(float l, float u, float v, float h, float s, float v2) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Hsv(h, s, v2); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(347.3767, 0.8314762, 0.6444615, 36.0555, 93.69012, 10.01514)] - public void Convert_Hsv_to_CieLuv(float h, float s, float v2, float l, float u, float v) - { - // Arrange - var input = new Hsv(h, s, v2); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs deleted file mode 100644 index 38449e4b7..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 30.19531, 46.4312, 11.16259)] - public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new HunterLab(l2, a, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(30.19531, 46.4312, 11.16259, 36.0555, 93.6901, 10.01514)] - public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) - { - // Arrange - var input = new HunterLab(l2, a, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs deleted file mode 100644 index f3bd93604..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.3729299, 0.01141088, 0.04014909)] - public void Convert_CieLuv_to_LinearRgb(float l, float u, float v, float r, float g, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.3729299, 0.01141088, 0.04014909, 36.0555, 93.6901, 10.01511)] - public void Convert_LinearRgb_to_CieLuv(float r, float g, float b, float l, float u, float v) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs deleted file mode 100644 index ac90a7ba4..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.164352, 0.03267485, 0.0483408)] - public void Convert_CieLuv_to_Lms(float l, float u, float v, float l2, float m, float s) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.164352, 0.03267485, 0.0483408, 36.0555, 93.69009, 10.01514)] - public void Convert_Lms_to_CieLuv(float l2, float m, float s, float l, float u, float v) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs deleted file mode 100644 index b2e308fce..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.6444615, 0.1086071, 0.2213444)] - public void Convert_CieLuv_to_Rgb(float l, float u, float v, float r, float g, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.6444615, 0.1086071, 0.2213444, 36.0555, 93.69012, 10.01514)] - public void Convert_Rgb_to_CieLuv(float r, float g, float b, float l, float u, float v) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs deleted file mode 100644 index f7c6372b1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 93.6901, 10.01514, 71.8283, 119.3174, 193.9839)] - public void Convert_CieLuv_to_YCbCr(float l, float u, float v, float y, float cb, float cr) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(71.8283, 119.3174, 193.9839, 36.00565, 93.44593, 10.2234)] - public void Convert_YCbCr_to_CieLuv(float y, float cb, float cr, float l, float u, float v) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs deleted file mode 100644 index bae914002..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.211263)] - public void Convert_CieXyy_to_Hsl(float x, float y, float yl, float h, float s, float l) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Hsl(h, s, l); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.211263, 0.3, 0.6, 0.1067051)] - public void Convert_Hsl_to_CieXyy(float h, float s, float l, float x, float y, float yl) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs deleted file mode 100644 index 7de91cc32..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.4225259)] - public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.4225259, 0.3, 0.6, 0.1067051)] - public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs deleted file mode 100644 index 40b58b704..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 31.46263, -32.81796, 28.64938)] - public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(31.46263, -32.81796, 28.64938, 0.3605552, 0.9369011, 0.1001514)] - public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) - { - // Arrange - var input = new HunterLab(l, a, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs deleted file mode 100644 index 062f54abc..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.1492062, 0)] - public void Convert_CieXyy_to_LinearRgb(float x, float y, float yl, float r, float g, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0.1492062, 0, 0.3, 0.6, 0.1067051)] - public void Convert_LinearRgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs deleted file mode 100644 index 8e9cbc5ff..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0.06631134, 0.1415282, -0.03809926)] - public void Convert_CieXyy_to_Lms(float x, float y, float yl, float l, float m, float s) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Lms(l, m, s); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.06631134, 0.1415282, -0.03809926, 0.360555, 0.9369009, 0.1001514)] - public void Convert_Lms_to_CieXyy(float l, float m, float s, float x, float y, float yl) - { - // Arrange - var input = new Lms(l, m, s); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs deleted file mode 100644 index 0a7cd6842..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.4225259, 0)] - public void Convert_CieXyy_to_Rgb(float x, float y, float yl, float r, float g, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0.4225259, 0, 0.3, 0.6, 0.1067051)] - public void Convert_Rgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs deleted file mode 100644 index efacebcf1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(0.360555, 0.936901, 0.1001514, 63.24579, 92.30826, 82.88884)] - public void Convert_CieXyy_to_YCbCr(float x, float y, float yl, float y2, float cb, float cr) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(63.24579, 92.30826, 82.88884, 0.3, 0.6, 0.1072441)] - public void Convert_YCbCr_to_CieXyy(float y2, float cb, float cr, float x, float y, float yl) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs deleted file mode 100644 index 3ea4228e5..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieLabConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 431.0345, 0, 0.95047, 0, 0)] - [InlineData(100, -431.0345, 172.4138, 0, 1, 0)] - [InlineData(0, 0, -172.4138, 0, 0, 1.08883)] - [InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] - [InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] - [InlineData(10, -400, 20, 0, 0.011260, 0)] - public void Convert_Lab_to_Xyz(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new CieLab(l, a, b, Illuminants.D65); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 0, 0, 0, 431.0345, 0)] - [InlineData(0, 1, 0, 100, -431.0345, 172.4138)] - [InlineData(0, 0, 1.08883, 0, 0, -172.4138)] - [InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] - public void Convert_Xyz_to_Lab(float x, float y, float z, float l, float a, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieLab(l, a, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = converter.ToCieLab(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs deleted file mode 100644 index 698c9add6..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.936901, 0.1001514, 97.50815, 155.8035, 139.323)] - public void Convert_CieXyz_to_CieLch(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(97.50815, 155.8035, 139.323, 0.3605551, 0.936901, 0.1001514)] - public void Convert_CieLch_to_CieXyz(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs deleted file mode 100644 index a4caf4c85..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndCieLchuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 183.3831, 133.6321)] - public void Convert_CieXyz_to_CieLchuv(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(97.50697, 183.3831, 133.6321, 0.360555, 0.936901, 0.1001515)] - public void Convert_CieLchuv_to_CieXyz(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs deleted file mode 100644 index 9e3381b40..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieLuvConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 100, 50, 0, 0, 0)] - [InlineData(0.1, 100, 50, 0.000493, 0.000111, 0)] - [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] - [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] - [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] - [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] - public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) - { - // Arrange - var input = new CieLuv(l, u, v, Illuminants.D65); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] - [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] - [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] - [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] - [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] - public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = converter.ToCieLuv(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs deleted file mode 100644 index 5ef5c4d21..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieXyyConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - [Theory] - [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] - [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] - [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] - [InlineData(0, 0, 0, 0.538842, 0.000000, 0.000000)] - public void Convert_xyY_to_XYZ(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) - { - var input = new CieXyy(x, y, yl); - var expected = new CieXyz(xyzX, xyzY, xyzZ); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = ColorSpaceConverter.ToCieXyz(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - [Theory] - [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] - [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] - [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] - [InlineData(0.231809, 0, 0.077528, 0.749374, 0.000000, 0.000000)] - public void Convert_XYZ_to_xyY(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) - { - var input = new CieXyz(xyzX, xyzY, xyzZ); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = ColorSpaceConverter.ToCieXyy(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs deleted file mode 100644 index 634d03a50..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)] - public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new Hsl(h, s, l); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.5, 0.3575761, 0.7151522, 0.119192)] - public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs deleted file mode 100644 index ccedd7b75..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.9999999)] - public void Convert_CieXyz_to_Hsv(float x, float y, float yl, float h, float s, float v) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.9999999, 0.3575761, 0.7151522, 0.119192)] - public void Convert_Hsv_to_CieXyz(float h, float s, float v, float x, float y, float yl) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs deleted file mode 100644 index af7087ba2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndHunterLabConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0 - public void Convert_HunterLab_to_Xyz(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.C }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) - public void Convert_HunterLab_to_Xyz_D65(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunterLab 100, 0, 0 (adaptation to C performed) - public void Convert_Xyz_D65_to_HunterLab(float x, float y, float z, float l, float a, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = converter.ToHunterLab(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs deleted file mode 100644 index 33bdc6e93..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using original colorful library. -/// -public class CieXyzAndLmsConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0.941428535, 1.040417467, 1.089532651, 0.95047, 1, 1.08883)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.850765697, -0.713042594, 0.036973283, 0.95047, 0, 0)] - [InlineData(0.2664, 1.7135, -0.0685, 0, 1, 0)] - [InlineData(-0.175737162, 0.039960061, 1.121059368, 0, 0, 1.08883)] - [InlineData(0.2262677362, 0.0961411609, 0.0484570397, 0.216938, 0.150041, 0.048850)] - public void Convert_Lms_to_CieXyz(float l, float m, float s, float x, float y, float z) - { - // Arrange - var input = new Lms(l, m, s); - var converter = new ColorSpaceConverter(); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0.95047, 1, 1.08883, 0.941428535, 1.040417467, 1.089532651)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 0, 0, 0.850765697, -0.713042594, 0.036973283)] - [InlineData(0, 1, 0, 0.2664, 1.7135, -0.0685)] - [InlineData(0, 0, 1.08883, -0.175737162, 0.039960061, 1.121059368)] - [InlineData(0.216938, 0.150041, 0.048850, 0.2262677362, 0.0961411609, 0.0484570397)] - public void Convert_CieXyz_to_Lms(float x, float y, float z, float l, float m, float s) - { - // Arrange - var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter(); - var expected = new Lms(l, m, s); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = converter.ToLms(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs deleted file mode 100644 index ba67e605a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(0.360555, 0.936901, 0.1001514, 149.685, 43.52769, 21.23457)] - public void Convert_CieXyz_to_YCbCr(float x, float y, float z, float y2, float cb, float cr) - { - // Arrange - var input = new CieXyz(x, y, z); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(149.685, 43.52769, 21.23457, 0.3575761, 0.7151522, 0.119192)] - public void Convert_YCbCr_to_CieXyz(float y2, float cb, float cr, float x, float y, float z) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs deleted file mode 100644 index 9def3e1b2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85025, 64.77041, 118.2425)] - public void Convert_Cmyk_to_CieLch(float c, float m, float y, float k, float l, float c2, float h) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLch(l, c2, h); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(100, 3.81656E-05, 218.6598, 0, 1.192093E-07, 0, 5.960464E-08)] - [InlineData(62.85025, 64.77041, 118.2425, 0.286581, 0, 0.7975187, 0.34983)] - public void Convert_CieLch_to_Cmyk(float l, float c2, float h, float c, float m, float y, float k) - { - // Arrange - var input = new CieLch(l, c2, h); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs deleted file mode 100644 index 9ae0fb711..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 100, -1.937151E-05, 0)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.66017, -24.01712, 68.29556)] - public void Convert_Cmyk_to_CieLuv(float c, float m, float y, float k, float l, float u, float v) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(100, -1.937151E-05, 0, 3.576279E-07, 0, 0, 5.960464E-08)] - [InlineData(62.66017, -24.01712, 68.29556, 0.2865804, 0, 0.7975189, 0.3498302)] - public void Convert_CieLuv_to_Cmyk(float l, float u, float v, float c, float m, float y, float k) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs deleted file mode 100644 index 270db9d20..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0.3127266, 0.3290231, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.3628971, 0.5289949, 0.3118104)] - public void Convert_Cmyk_to_CieXyy(float c, float m, float y, float k, float x, float y2, float yl) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieXyy(x, y2, yl); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.3127266, 0.3290231, 1, 0, 0, 0, 5.960464E-08)] - [InlineData(0.3628971, 0.5289949, 0.3118104, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_CieXyy_to_Cmyk(float x, float y2, float yl, float c, float m, float y, float k) - { - // Arrange - var input = new CieXyy(x, y2, yl); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs deleted file mode 100644 index 972948c71..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieXyzConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0.9504699, 1, 1.08883)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.2139058, 0.3118104, 0.0637231)] - public void Convert_Cmyk_to_CieXyz(float c, float m, float y, float k, float x, float y2, float z) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieXyz(x, y2, z); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.9504699, 1, 1.08883, 1.192093E-07, 0, 0, 5.960464E-08)] - [InlineData(0.2139058, 0.3118104, 0.0637231, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_CieXyz_to_Cmyk(float x, float y2, float z, float c, float m, float y, float k) - { - // Arrange - var input = new CieXyz(x, y2, z); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs deleted file mode 100644 index 2b00942ac..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.6632275, 0.3909085)] - public void Convert_Cmyk_to_Hsl(float c, float m, float y, float k, float h, float s, float l) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Hsl(h, s, l); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = ColorSpaceConverter.ToHsl(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 1, 0, 0, 0, 0)] - [InlineData(81.56041, 0.6632275, 0.3909085, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_Hsl_to_Cmyk(float h, float s, float l, float c, float m, float y, float k) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs deleted file mode 100644 index f158fb5f9..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.7975187, 0.6501698)] - public void Convert_Cmyk_to_Hsv(float c, float m, float y, float k, float h, float s, float v) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Hsv(h, s, v); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = ColorSpaceConverter.ToHsv(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 1, 0, 0, 0, 0)] - [InlineData(81.56041, 0.7975187, 0.6501698, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_Hsv_to_Cmyk(float h, float s, float v, float c, float m, float y, float k) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs deleted file mode 100644 index 9832a0d71..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 99.99999, 0, -1.66893E-05)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 55.66742, -27.21679, 31.73834)] - public void Convert_Cmyk_to_HunterLab(float c, float m, float y, float k, float l, float a, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(99.99999, 0, -1.66893E-05, 1.192093E-07, 1.192093E-07, 0, 5.960464E-08)] - [InlineData(55.66742, -27.21679, 31.73834, 0.2865806, 0, 0.7975186, 0.3498301)] - public void Convert_HunterLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) - { - // Arrange - var input = new HunterLab(l, a, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs deleted file mode 100644 index 1e8bc52e2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 255, 128, 128)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] - public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = ColorSpaceConverter.ToYCbCr(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] - [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] - public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs deleted file mode 100644 index bbebf96b3..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests methods. -/// Test data generated using: -/// -/// -/// -public class ColorConverterAdaptTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] - public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb); - var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb); - var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - - // Action - Rgb actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] - public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb); - var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb); - var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb }; - var converter = new ColorSpaceConverter(options); - - // Action - Rgb actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] - public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) - { - // Arrange - var input = new CieLab(l1, a1, b1, Illuminants.D65); - var expected = new CieLab(l2, a2, b2); - var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLab actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] - public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expected = new CieXyz(x2, y2, z2); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - CieXyz actual = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] - public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expected = new CieXyz(x2, y2, z2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - WhitePoint = Illuminants.D50 - }; - - var converter = new ColorSpaceConverter(options); - - // Action - CieXyz actual = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22.1090755, 32.2102661, 1.153463)] - public void Adapt_HunterLab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) - { - // Arrange - var input = new HunterLab(l1, a1, b1, Illuminants.D65); - var expected = new HunterLab(l2, a2, b2); - var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - HunterLab actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22, 33, 0.9999999)] - public void Adapt_CieLchuv_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) - { - // Arrange - var input = new CieLchuv(l1, c1, h1, Illuminants.D65); - var expected = new CieLchuv(l2, c2, h2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - TargetLabWhitePoint = Illuminants.D50 - }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLchuv actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(22, 33, 1, 22, 33, 0.9999999)] - public void Adapt_CieLch_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) - { - // Arrange - var input = new CieLch(l1, c1, h1, Illuminants.D65); - var expected = new CieLch(l2, c2, h2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - TargetLabWhitePoint = Illuminants.D50 - }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLch actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs deleted file mode 100644 index 0119f4f3a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class RgbAndCieXyzConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from () - /// to (default sRGB working space). - /// - [Theory] - [InlineData(0.96422, 1.00000, 0.82521, 1, 1, 1)] - [InlineData(0.00000, 1.00000, 0.00000, 0, 1, 0)] - [InlineData(0.96422, 0.00000, 0.00000, 1, 0, 0.292064)] - [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] - public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = converter.ToRgb(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion - /// from () - /// to (default sRGB working space). - /// - [Theory] - [InlineData(0.950470, 1.000000, 1.088830, 1, 1, 1)] - [InlineData(0, 1.000000, 0, 0, 1, 0)] - [InlineData(0.950470, 0, 0, 1, 0, 0.254967)] - [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] - public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = converter.ToRgb(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from (default sRGB working space) - /// to (). - /// - [Theory] - [InlineData(1, 1, 1, 0.964220, 1.000000, 0.825210)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0, 0.436075, 0.222504, 0.013932)] - [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] - [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] - [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] - public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) - { - // Arrange - var input = new Rgb(r, g, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from (default sRGB working space) - /// to (). - /// - [Theory] - [InlineData(1, 1, 1, 0.950470, 1.000000, 1.088830)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0, 0.412456, 0.212673, 0.019334)] - [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] - [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] - [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] - public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) - { - // Arrange - var input = new Rgb(r, g, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs deleted file mode 100644 index 942b34db3..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -/// -public class RgbAndCmykConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(1, 1, 1, 1, 0, 0, 0)] - [InlineData(0, 0, 0, 0, 1, 1, 1)] - [InlineData(0, 0.84, 0.037, 0.365, 0.635, 0.1016, 0.6115)] - public void Convert_Cmyk_To_Rgb(float c, float m, float y, float k, float r, float g, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(1, 1, 1, 0, 0, 0, 0)] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.635, 0.1016, 0.6115, 0, 0.84, 0.037, 0.365)] - public void Convert_Rgb_To_Cmyk(float r, float g, float b, float c, float m, float y, float k) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs deleted file mode 100644 index 8b448a042..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -/// -public class RgbAndHslConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 1, 1, 1, 1, 1)] - [InlineData(360, 1, 1, 1, 1, 1)] - [InlineData(0, 1, .5F, 1, 0, 0)] - [InlineData(120, 1, .5F, 0, 1, 0)] - [InlineData(240, 1, .5F, 0, 0, 1)] - public void Convert_Hsl_To_Rgb(float h, float s, float l, float r, float g, float b) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 0, 0, 1)] - [InlineData(1, 0, 0, 0, 1, .5F)] - [InlineData(0, 1, 0, 120, 1, .5F)] - [InlineData(0, 0, 1, 240, 1, .5F)] - [InlineData(0.7, 0.8, 0.6, 90, 0.3333, 0.7F)] - public void Convert_Rgb_To_Hsl(float r, float g, float b, float h, float s, float l) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Hsl(h, s, l); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = ColorSpaceConverter.ToHsl(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs deleted file mode 100644 index d80aa6c32..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class RgbAndHsvConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0, 1, 1, 1, 1)] - [InlineData(360, 1, 1, 1, 0, 0)] - [InlineData(0, 1, 1, 1, 0, 0)] - [InlineData(120, 1, 1, 0, 1, 0)] - [InlineData(240, 1, 1, 0, 0, 1)] - public void Convert_Hsv_To_Rgb(float h, float s, float v, float r, float g, float b) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 0, 0, 1)] - [InlineData(1, 0, 0, 0, 1, 1)] - [InlineData(0, 1, 0, 120, 1, 1)] - [InlineData(0, 0, 1, 240, 1, 1)] - public void Convert_Rgb_To_Hsv(float r, float g, float b, float h, float s, float v) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Hsv(h, s, v); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = ColorSpaceConverter.ToHsv(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs deleted file mode 100644 index eb4eb0bbf..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated mathematically -/// -public class RgbAndYCbCrConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(255, 128, 128, 1, 1, 1)] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(128, 128, 128, 0.502, 0.502, 0.502)] - public void Convert_YCbCr_To_Rgb(float y, float cb, float cr, float r, float g, float b) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new Rgb(r, g, b); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(1, 1, 1, 255, 128, 128)] - [InlineData(0.5, 0.5, 0.5, 127.5, 128, 128)] - [InlineData(1, 0, 0, 76.245, 84.972, 255)] - public void Convert_Rgb_To_YCbCr(float r, float g, float b, float y, float cb, float cr) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = ColorSpaceConverter.ToYCbCr(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs deleted file mode 100644 index 9e33ad168..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -public class VonKriesChromaticAdaptationTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - public static readonly TheoryData WhitePoints = new TheoryData - { - { CieLuv.DefaultWhitePoint, CieLab.DefaultWhitePoint }, - { CieLuv.DefaultWhitePoint, CieLuv.DefaultWhitePoint } - }; - - [Theory] - [MemberData(nameof(WhitePoints))] - public void SingleAndBulkTransformYieldIdenticalResults(CieXyz sourceWhitePoint, CieXyz destinationWhitePoint) - { - var adaptation = new VonKriesChromaticAdaptation(); - var input = new CieXyz(1, 0, 1); - CieXyz expected = adaptation.Transform(input, sourceWhitePoint, destinationWhitePoint); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - adaptation.Transform(inputSpan, actualSpan, sourceWhitePoint, destinationWhitePoint); - - for (int i = 0; i < inputSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HslTests.cs b/tests/ImageSharp.Tests/Colorspaces/HslTests.cs deleted file mode 100644 index a8702488a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HslTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HslTests -{ - [Fact] - public void HslConstructorAssignsFields() - { - const float h = 275F; - const float s = .64F; - const float l = .87F; - var hsl = new Hsl(h, s, l); - - Assert.Equal(h, hsl.H); - Assert.Equal(s, hsl.S); - Assert.Equal(l, hsl.L); - } - - [Fact] - public void HslEquality() - { - var x = default(Hsl); - var y = new Hsl(Vector3.One); - - Assert.True(default(Hsl) == default(Hsl)); - Assert.False(default(Hsl) != default(Hsl)); - Assert.Equal(default(Hsl), default(Hsl)); - Assert.Equal(new Hsl(1, 0, 1), new Hsl(1, 0, 1)); - Assert.Equal(new Hsl(Vector3.One), new Hsl(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs b/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs deleted file mode 100644 index caedd3f17..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HsvTests -{ - [Fact] - public void HsvConstructorAssignsFields() - { - const float h = 275F; - const float s = .64F; - const float v = .87F; - var hsv = new Hsv(h, s, v); - - Assert.Equal(h, hsv.H); - Assert.Equal(s, hsv.S); - Assert.Equal(v, hsv.V); - } - - [Fact] - public void HsvEquality() - { - var x = default(Hsv); - var y = new Hsv(Vector3.One); - - Assert.True(default(Hsv) == default(Hsv)); - Assert.False(default(Hsv) != default(Hsv)); - Assert.Equal(default(Hsv), default(Hsv)); - Assert.Equal(new Hsv(1, 0, 1), new Hsv(1, 0, 1)); - Assert.Equal(new Hsv(Vector3.One), new Hsv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs deleted file mode 100644 index 9c97c4c91..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HunterLabTests -{ - [Fact] - public void HunterLabConstructorAssignsFields() - { - const float l = 75F; - const float a = -64F; - const float b = 87F; - var hunterLab = new HunterLab(l, a, b); - - Assert.Equal(l, hunterLab.L); - Assert.Equal(a, hunterLab.A); - Assert.Equal(b, hunterLab.B); - } - - [Fact] - public void HunterLabEquality() - { - var x = default(HunterLab); - var y = new HunterLab(Vector3.One); - - Assert.True(default(HunterLab) == default(HunterLab)); - Assert.True(new HunterLab(1, 0, 1) != default(HunterLab)); - Assert.False(new HunterLab(1, 0, 1) == default(HunterLab)); - Assert.Equal(default(HunterLab), default(HunterLab)); - Assert.Equal(new HunterLab(1, 0, 1), new HunterLab(1, 0, 1)); - Assert.Equal(new HunterLab(Vector3.One), new HunterLab(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs deleted file mode 100644 index ff2d15134..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class LinearRgbTests -{ - [Fact] - public void LinearRgbConstructorAssignsFields() - { - const float r = .75F; - const float g = .64F; - const float b = .87F; - var rgb = new LinearRgb(r, g, b); - - Assert.Equal(r, rgb.R); - Assert.Equal(g, rgb.G); - Assert.Equal(b, rgb.B); - } - - [Fact] - public void LinearRgbEquality() - { - var x = default(LinearRgb); - var y = new LinearRgb(Vector3.One); - - Assert.True(default(LinearRgb) == default(LinearRgb)); - Assert.False(default(LinearRgb) != default(LinearRgb)); - Assert.Equal(default(LinearRgb), default(LinearRgb)); - Assert.Equal(new LinearRgb(1, 0, 1), new LinearRgb(1, 0, 1)); - Assert.Equal(new LinearRgb(Vector3.One), new LinearRgb(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs b/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs deleted file mode 100644 index 5e8840664..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class LmsTests -{ - [Fact] - public void LmsConstructorAssignsFields() - { - const float l = 75F; - const float m = -64F; - const float s = 87F; - var lms = new Lms(l, m, s); - - Assert.Equal(l, lms.L); - Assert.Equal(m, lms.M); - Assert.Equal(s, lms.S); - } - - [Fact] - public void LmsEquality() - { - var x = default(Lms); - var y = new Lms(Vector3.One); - - Assert.True(default(Lms) == default(Lms)); - Assert.True(new Lms(1, 0, 1) != default(Lms)); - Assert.False(new Lms(1, 0, 1) == default(Lms)); - Assert.Equal(default(Lms), default(Lms)); - Assert.Equal(new Lms(1, 0, 1), new Lms(1, 0, 1)); - Assert.Equal(new Lms(Vector3.One), new Lms(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs deleted file mode 100644 index be96b79f4..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class RgbTests -{ - [Fact] - public void RgbConstructorAssignsFields() - { - const float r = .75F; - const float g = .64F; - const float b = .87F; - var rgb = new Rgb(r, g, b); - - Assert.Equal(r, rgb.R); - Assert.Equal(g, rgb.G); - Assert.Equal(b, rgb.B); - } - - [Fact] - public void RgbEquality() - { - var x = default(Rgb); - var y = new Rgb(Vector3.One); - - Assert.True(default(Rgb) == default(Rgb)); - Assert.False(default(Rgb) != default(Rgb)); - Assert.Equal(default(Rgb), default(Rgb)); - Assert.Equal(new Rgb(1, 0, 1), new Rgb(1, 0, 1)); - Assert.Equal(new Rgb(Vector3.One), new Rgb(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } - - [Fact] - public void RgbAndRgb24Operators() - { - const byte r = 64; - const byte g = 128; - const byte b = 255; - - Rgb24 rgb24 = new Rgb(r / 255F, g / 255F, b / 255F); - Rgb rgb2 = rgb24; - - Assert.Equal(r, rgb24.R); - Assert.Equal(g, rgb24.G); - Assert.Equal(b, rgb24.B); - - Assert.Equal(r / 255F, rgb2.R); - Assert.Equal(g / 255F, rgb2.G); - Assert.Equal(b / 255F, rgb2.B); - } - - [Fact] - public void RgbAndRgba32Operators() - { - const byte r = 64; - const byte g = 128; - const byte b = 255; - - Rgba32 rgba32 = new Rgb(r / 255F, g / 255F, b / 255F); - Rgb rgb2 = rgba32; - - Assert.Equal(r, rgba32.R); - Assert.Equal(g, rgba32.G); - Assert.Equal(b, rgba32.B); - - Assert.Equal(r / 255F, rgb2.R); - Assert.Equal(g / 255F, rgb2.G); - Assert.Equal(b / 255F, rgb2.B); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs deleted file mode 100644 index 2ae0824f2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -public class StringRepresentationTests -{ - private static readonly Vector3 One = new Vector3(1); - private static readonly Vector3 Zero = new Vector3(0); - private static readonly Vector3 Random = new Vector3(42.4F, 94.5F, 83.4F); - - public static readonly TheoryData TestData = new TheoryData - { - { new CieLab(Zero), "CieLab(0, 0, 0)" }, - { new CieLch(Zero), "CieLch(0, 0, 0)" }, - { new CieLchuv(Zero), "CieLchuv(0, 0, 0)" }, - { new CieLuv(Zero), "CieLuv(0, 0, 0)" }, - { new CieXyz(Zero), "CieXyz(0, 0, 0)" }, - { new CieXyy(Zero), "CieXyy(0, 0, 0)" }, - { new HunterLab(Zero), "HunterLab(0, 0, 0)" }, - { new Lms(Zero), "Lms(0, 0, 0)" }, - { new LinearRgb(Zero), "LinearRgb(0, 0, 0)" }, - { new Rgb(Zero), "Rgb(0, 0, 0)" }, - { new Hsl(Zero), "Hsl(0, 0, 0)" }, - { new Hsv(Zero), "Hsv(0, 0, 0)" }, - { new YCbCr(Zero), "YCbCr(0, 0, 0)" }, - { new CieLab(One), "CieLab(1, 1, 1)" }, - { new CieLch(One), "CieLch(1, 1, 1)" }, - { new CieLchuv(One), "CieLchuv(1, 1, 1)" }, - { new CieLuv(One), "CieLuv(1, 1, 1)" }, - { new CieXyz(One), "CieXyz(1, 1, 1)" }, - { new CieXyy(One), "CieXyy(1, 1, 1)" }, - { new HunterLab(One), "HunterLab(1, 1, 1)" }, - { new Lms(One), "Lms(1, 1, 1)" }, - { new LinearRgb(One), "LinearRgb(1, 1, 1)" }, - { new Rgb(One), "Rgb(1, 1, 1)" }, - { new Hsl(One), "Hsl(1, 1, 1)" }, - { new Hsv(One), "Hsv(1, 1, 1)" }, - { new YCbCr(One), "YCbCr(1, 1, 1)" }, - { new CieXyChromaticityCoordinates(1, 1), "CieXyChromaticityCoordinates(1, 1)" }, - { new CieLab(Random), "CieLab(42.4, 94.5, 83.4)" }, - { new CieLch(Random), "CieLch(42.4, 94.5, 83.4)" }, - { new CieLchuv(Random), "CieLchuv(42.4, 94.5, 83.4)" }, - { new CieLuv(Random), "CieLuv(42.4, 94.5, 83.4)" }, - { new CieXyz(Random), "CieXyz(42.4, 94.5, 83.4)" }, - { new CieXyy(Random), "CieXyy(42.4, 94.5, 83.4)" }, - { new HunterLab(Random), "HunterLab(42.4, 94.5, 83.4)" }, - { new Lms(Random), "Lms(42.4, 94.5, 83.4)" }, - { new LinearRgb(Random), "LinearRgb(1, 1, 1)" }, // clamping to 1 is expected - { new Rgb(Random), "Rgb(1, 1, 1)" }, // clamping to 1 is expected - { new Hsl(Random), "Hsl(42.4, 1, 1)" }, // clamping to 1 is expected - { new Hsv(Random), "Hsv(42.4, 1, 1)" }, // clamping to 1 is expected - { new YCbCr(Random), "YCbCr(42.4, 94.5, 83.4)" }, - }; - - [Theory] - [MemberData(nameof(TestData))] - public void StringRepresentationsAreCorrect(object color, string text) => Assert.Equal(text, color.ToString()); -} diff --git a/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs b/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs deleted file mode 100644 index efab45c3c..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class YCbCrTests -{ - [Fact] - public void YCbCrConstructorAssignsFields() - { - const float y = 75F; - const float cb = 64F; - const float cr = 87F; - var yCbCr = new YCbCr(y, cb, cr); - - Assert.Equal(y, yCbCr.Y); - Assert.Equal(cb, yCbCr.Cb); - Assert.Equal(cr, yCbCr.Cr); - } - - [Fact] - public void YCbCrEquality() - { - var x = default(YCbCr); - var y = new YCbCr(Vector3.One); - - Assert.True(default(YCbCr) == default(YCbCr)); - Assert.False(default(YCbCr) != default(YCbCr)); - Assert.Equal(default(YCbCr), default(YCbCr)); - Assert.Equal(new YCbCr(1, 0, 1), new YCbCr(1, 0, 1)); - Assert.Equal(new YCbCr(Vector3.One), new YCbCr(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} From b458ff2fa024ae478594aa03a8a70f0a6cdb7e4c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 15:48:23 +1000 Subject: [PATCH 146/220] Optimize and cleanup per review --- src/ImageSharp/ColorProfiles/CieLab.cs | 18 ++------ src/ImageSharp/ColorProfiles/CieLch.cs | 10 ++-- src/ImageSharp/ColorProfiles/CieLchuv.cs | 18 ++------ src/ImageSharp/ColorProfiles/CieLuv.cs | 10 ++-- .../CieXyChromaticityCoordinates.cs | 7 +-- src/ImageSharp/ColorProfiles/CieXyy.cs | 10 ++-- src/ImageSharp/ColorProfiles/CieXyz.cs | 5 +- src/ImageSharp/ColorProfiles/Cmyk.cs | 13 ++---- src/ImageSharp/ColorProfiles/Hsl.cs | 10 ++-- src/ImageSharp/ColorProfiles/Hsv.cs | 10 ++-- src/ImageSharp/ColorProfiles/HunterLab.cs | 22 ++++----- .../ColorProfiles/KnownIlluminants.cs | 46 +++++++++---------- src/ImageSharp/ColorProfiles/Lms.cs | 14 +++--- src/ImageSharp/ColorProfiles/Rgb.cs | 10 ++-- .../RgbPrimariesChromaticityCoordinates.cs | 2 +- src/ImageSharp/ColorProfiles/YCbCr.cs | 10 ++-- 16 files changed, 87 insertions(+), 128 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 72148af45..82d60f4da 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -12,12 +12,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; ///
    public readonly struct CieLab : IProfileConnectingSpace { - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = KnownIlluminants.D50; - /// /// Initializes a new instance of the struct. /// @@ -50,19 +44,19 @@ public readonly struct CieLab : IProfileConnectingSpace /// Gets the lightness dimension. /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). ///
    - public readonly float L { get; } + public float L { get; } /// /// Gets the a color component. /// A value usually ranging from -100 to 100. Negative is green, positive magenta. /// - public readonly float A { get; } + public float A { get; } /// /// Gets the b color component. /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow /// - public readonly float B { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -97,10 +91,8 @@ public readonly struct CieLab : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLab other) => - this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B); + public bool Equals(CieLab other) + => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index a8d70cfb7..8f6298a2d 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -44,19 +44,19 @@ public readonly struct CieLch : IColorProfile /// Gets the lightness dimension. /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a chroma component. /// A value ranging from 0 to 200. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the h° hue component in degrees. /// A value ranging from 0 to 360. /// - public readonly float H { get; } + public float H { get; } /// /// Compares two objects for equality. @@ -94,9 +94,7 @@ public readonly struct CieLch : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLch other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H); + => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); /// public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs index f8f62d104..e537259e4 100644 --- a/src/ImageSharp/ColorProfiles/CieLchuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -45,24 +45,19 @@ public readonly struct CieLchuv : IColorProfile /// Gets the lightness dimension. /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a chroma component. /// A value ranging from 0 to 200. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the h° hue component in degrees. /// A value ranging from 0 to 360. /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } + public float H { get; } /// /// Compares two objects for equality. @@ -151,7 +146,7 @@ public readonly struct CieLchuv : IColorProfile /// public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); + => HashCode.Combine(this.L, this.C, this.H); /// public override string ToString() @@ -164,10 +159,7 @@ public readonly struct CieLchuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLchuv other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); + => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); /// /// Computes the saturation of the color (chroma normalized by lightness) diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 9bf102057..118d32f04 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -46,19 +46,19 @@ public readonly struct CieLuv : IColorProfile /// Gets the lightness dimension /// A value usually ranging between 0 and 100. /// - public readonly float L { get; } + public float L { get; } /// /// Gets the blue-yellow chromaticity coordinate of the given white point. /// A value usually ranging between -100 and 100. /// - public readonly float U { get; } + public float U { get; } /// /// Gets the red-green chromaticity coordinate of the given white point. /// A value usually ranging between -100 and 100. /// - public readonly float V { get; } + public float V { get; } /// /// Compares two objects for equality. @@ -205,9 +205,7 @@ public readonly struct CieLuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLuv other) - => this.L.Equals(other.L) - && this.U.Equals(other.U) - && this.V.Equals(other.V); + => new Vector3(this.L, this.U, this.V) == new Vector3(other.L, other.U, other.V); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs index b55abdd05..aad7dbf95 100644 --- a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; // ReSharper disable CompareOfFloatsByEqualityOperator @@ -29,7 +30,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable /// Ranges usually from 0 to 1. /// - public readonly float X { get; } + public float X { get; } /// /// Gets the chromaticity Y-coordinate @@ -37,7 +38,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable /// Ranges usually from 0 to 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Compares two objects for equality. @@ -79,5 +80,5 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyChromaticityCoordinates other) - => this.X.Equals(other.X) && this.Y.Equals(other.Y); + => new Vector2(this.X, this.Y) == new Vector2(other.X, other.Y); } diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index 4dcb582d6..ea19f5740 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -45,19 +45,19 @@ public readonly struct CieXyy : IColorProfile /// Gets the X chrominance component. /// A value usually ranging between 0 and 1. /// - public readonly float X { get; } + public float X { get; } /// /// Gets the Y chrominance component. /// A value usually ranging between 0 and 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the Y luminance component. /// A value usually ranging between 0 and 1. /// - public readonly float Yl { get; } + public float Yl { get; } /// /// Compares two objects for equality. @@ -150,7 +150,5 @@ public readonly struct CieXyy : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Yl.Equals(other.Yl); + => new Vector3(this.X, this.Y, this.Yl) == new Vector3(other.X, other.Y, other.Yl); } diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index fbd3e77d7..fe8755454 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.ColorProfiles; @@ -99,9 +98,7 @@ public readonly struct CieXyz : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyz other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Z.Equals(other.Z); + => new Vector3(this.X, this.Y, this.Z) == new Vector3(other.X, other.Y, other.Z); /// public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs index d586ab6d4..88d3249b3 100644 --- a/src/ImageSharp/ColorProfiles/Cmyk.cs +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -45,25 +45,25 @@ public readonly struct Cmyk : IColorProfile /// Gets the cyan color component. /// A value ranging between 0 and 1. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the magenta color component. /// A value ranging between 0 and 1. /// - public readonly float M { get; } + public float M { get; } /// /// Gets the yellow color component. /// A value ranging between 0 and 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the keyline black color component. /// A value ranging between 0 and 1. /// - public readonly float K { get; } + public float K { get; } /// /// Compares two objects for equality. @@ -157,8 +157,5 @@ public readonly struct Cmyk : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Cmyk other) - => this.C.Equals(other.C) - && this.M.Equals(other.M) - && this.Y.Equals(other.Y) - && this.K.Equals(other.K); + => new Vector4(this.C, this.M, this.Y, this.K) == new Vector4(other.C, other.M, other.Y, other.K); } diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index 8dcef23c4..c7ae97c79 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -43,19 +43,19 @@ public readonly struct Hsl : IColorProfile /// Gets the hue component. /// A value ranging between 0 and 360. /// - public readonly float H { get; } + public float H { get; } /// /// Gets the saturation component. /// A value ranging between 0 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Gets the lightness component. /// A value ranging between 0 and 1. /// - public readonly float L { get; } + public float L { get; } /// /// Compares two objects for equality. @@ -200,9 +200,7 @@ public readonly struct Hsl : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsl other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.L.Equals(other.L); + => new Vector3(this.H, this.S, this.L) == new Vector3(other.H, other.S, other.L); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetColorComponent(float first, float second, float third) diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index b3922d11f..83445a40f 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -43,19 +43,19 @@ public readonly struct Hsv : IColorProfile /// Gets the hue component. /// A value ranging between 0 and 360. /// - public readonly float H { get; } + public float H { get; } /// /// Gets the saturation component. /// A value ranging between 0 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Gets the value (brightness) component. /// A value ranging between 0 and 1. /// - public readonly float V { get; } + public float V { get; } /// /// Compares two objects for equality. @@ -223,7 +223,5 @@ public readonly struct Hsv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.V.Equals(other.V); + => new Vector3(this.H, this.S, this.V) == new Vector3(other.H, other.S, other.V); } diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index 5d6c01a2b..eb33e6a95 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -20,8 +20,10 @@ public readonly struct HunterLab : IColorProfile /// The b (blue - yellow) component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(float l, float a, float b) - : this(new Vector3(l, a, b)) { + this.L = l; + this.A = a; + this.B = b; } /// @@ -41,24 +43,19 @@ public readonly struct HunterLab : IColorProfile /// Gets the lightness dimension. /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a color component. /// A value usually ranging from -100 to 100. Negative is green, positive magenta. /// - public readonly float A { get; } + public float A { get; } /// /// Gets the b color component. /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color. - /// - public readonly CieXyz WhitePoint { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -162,7 +159,7 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); /// public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); @@ -173,10 +170,7 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HunterLab other) - => this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); + => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) diff --git a/src/ImageSharp/ColorProfiles/KnownIlluminants.cs b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs index ac5cbea2c..b9236497f 100644 --- a/src/ImageSharp/ColorProfiles/KnownIlluminants.cs +++ b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -15,57 +15,57 @@ namespace SixLabors.ImageSharp.ColorProfiles; public static class KnownIlluminants { /// - /// Incandescent / Tungsten + /// Gets the Incandescent / Tungsten illuminant. /// - public static readonly CieXyz A = new(1.09850F, 1F, 0.35585F); + public static CieXyz A { get; } = new(1.09850F, 1F, 0.35585F); /// - /// Direct sunlight at noon (obsoleteF) + /// Gets the Direct sunlight at noon (obsoleteF) illuminant. /// - public static readonly CieXyz B = new(0.99072F, 1F, 0.85223F); + public static CieXyz B { get; } = new(0.99072F, 1F, 0.85223F); /// - /// Average / North sky Daylight (obsoleteF) + /// Gets the Average / North sky Daylight (obsoleteF) illuminant. /// - public static readonly CieXyz C = new(0.98074F, 1F, 1.18232F); + public static CieXyz C { get; } = new(0.98074F, 1F, 1.18232F); /// - /// Horizon Light. ICC profile PCS + /// Gets the Horizon Light. ICC profile PCS illuminant. /// - public static readonly CieXyz D50 = new(0.96422F, 1F, 0.82521F); + public static CieXyz D50 { get; } = new(0.96422F, 1F, 0.82521F); /// - /// Mid-morning / Mid-afternoon Daylight + /// Gets the Mid-morning / Mid-afternoon Daylight illuminant. /// - public static readonly CieXyz D55 = new(0.95682F, 1F, 0.92149F); + public static CieXyz D55 { get; } = new(0.95682F, 1F, 0.92149F); /// - /// Noon Daylight: TelevisionF, sRGB color space + /// Gets the Noon Daylight: TelevisionF, sRGB color space illuminant. /// - public static readonly CieXyz D65 = new(0.95047F, 1F, 1.08883F); + public static CieXyz D65 { get; } = new(0.95047F, 1F, 1.08883F); /// - /// North sky Daylight + /// Gets the North sky Daylight illuminant. /// - public static readonly CieXyz D75 = new(0.94972F, 1F, 1.22638F); + public static CieXyz D75 { get; } = new(0.94972F, 1F, 1.22638F); /// - /// Equal energy + /// Gets the Equal energy illuminant. /// - public static readonly CieXyz E = new(1F, 1F, 1F); + public static CieXyz E { get; } = new(1F, 1F, 1F); /// - /// Cool White Fluorescent + /// Gets the Cool White Fluorescent illuminant. /// - public static readonly CieXyz F2 = new(0.99186F, 1F, 0.67393F); + public static CieXyz F2 { get; } = new(0.99186F, 1F, 0.67393F); /// - /// D65 simulatorF, Daylight simulator + /// Gets the D65 simulatorF, Daylight simulator illuminant. /// - public static readonly CieXyz F7 = new(0.95041F, 1F, 1.08747F); + public static CieXyz F7 { get; } = new(0.95041F, 1F, 1.08747F); /// - /// Philips TL84F, Ultralume 40 + /// Gets the Philips TL84F, Ultralume 40 illuminant. /// - public static readonly CieXyz F11 = new(1.00962F, 1F, 0.64350F); + public static CieXyz F11 { get; } = new(1.00962F, 1F, 0.64350F); } diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index cb060185d..03a1c5d66 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -16,8 +16,10 @@ internal readonly struct Lms : IColorProfile /// S represents the responsivity at short wavelengths. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lms(float l, float m, float s) - : this(new Vector3(l, m, s)) { + this.L = l; + this.M = m; + this.S = s; } /// @@ -37,19 +39,19 @@ internal readonly struct Lms : IColorProfile /// Gets the L long component. /// A value usually ranging between -1 and 1. /// - public readonly float L { get; } + public float L { get; } /// /// Gets the M medium component. /// A value usually ranging between -1 and 1. /// - public readonly float M { get; } + public float M { get; } /// /// Gets the S short component. /// A value usually ranging between -1 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Compares two objects for equality. @@ -92,9 +94,7 @@ internal readonly struct Lms : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Lms other) - => this.L.Equals(other.L) - && this.M.Equals(other.M) - && this.S.Equals(other.S); + => new Vector3(this.L, this.M, this.S) == new Vector3(other.L, other.M, other.S); /// public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 697c0fbd8..8036d83e5 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -43,19 +43,19 @@ public readonly struct Rgb : IProfileConnectingSpace /// Gets the red component. /// A value usually ranging between 0 and 1. /// - public readonly float R { get; } + public float R { get; } /// /// Gets the green component. /// A value usually ranging between 0 and 1. /// - public readonly float G { get; } + public float G { get; } /// /// Gets the blue component. /// A value usually ranging between 0 and 1. /// - public readonly float B { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -91,9 +91,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); + => new Vector3(this.R, this.G, this.B) == new Vector3(other.R, other.G, other.B); /// public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs index 90a5a57f9..1040f23ac 100644 --- a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index 5f0b4118a..b9f5f65ee 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -45,19 +45,19 @@ public readonly struct YCbCr : IColorProfile /// Gets the Y luminance component. /// A value ranging between 0 and 255. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the Cb chroma component. /// A value ranging between 0 and 255. /// - public readonly float Cb { get; } + public float Cb { get; } /// /// Gets the Cr chroma component. /// A value ranging between 0 and 255. /// - public readonly float Cr { get; } + public float Cr { get; } /// /// Compares two objects for equality. @@ -152,7 +152,5 @@ public readonly struct YCbCr : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) - => this.Y.Equals(other.Y) - && this.Cb.Equals(other.Cb) - && this.Cr.Equals(other.Cr); + => new Vector3(this.Y, this.Cb, this.Cr) == new Vector3(other.Y, other.Cb, other.Cr); } From eda85d20eef0d11391ebe50e5405fc57621b3f1c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 21:36:59 +1000 Subject: [PATCH 147/220] Define new joining types and extend existing pixel info --- .../Formats/AnimatedImageFrameMetadata.cs | 62 ----- .../Formats/FormatConnectingFrameMetadata.cs | 35 +++ .../Formats/FormatConnectingMetadata.cs | 65 +++++ src/ImageSharp/Formats/FrameBlendMode.cs | 23 ++ src/ImageSharp/Formats/FrameColorTableMode.cs | 20 ++ src/ImageSharp/Formats/FrameDisposalMode.cs | 38 +++ src/ImageSharp/Formats/Gif/GifMetadata.cs | 2 +- .../Formats/IFormatFrameMetadata.cs | 33 +++ src/ImageSharp/Formats/IFormatMetadata.cs | 33 +++ .../Jpeg/Components/Encoder/HuffmanSpec.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 18 +- src/ImageSharp/Formats/Png/PngMetadata.cs | 4 +- src/ImageSharp/PixelFormats/PixelColorType.cs | 62 ++++- .../PixelFormats/PixelComponentInfo.cs | 4 +- .../PixelFormats/PixelColorTypeTests.cs | 255 ++++++++++++++++++ 15 files changed, 573 insertions(+), 83 deletions(-) create mode 100644 src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/FormatConnectingMetadata.cs create mode 100644 src/ImageSharp/Formats/FrameBlendMode.cs create mode 100644 src/ImageSharp/Formats/FrameColorTableMode.cs create mode 100644 src/ImageSharp/Formats/FrameDisposalMode.cs create mode 100644 src/ImageSharp/Formats/IFormatFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/IFormatMetadata.cs create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs diff --git a/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs index 75595e1f7..8f8e18740 100644 --- a/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs +++ b/src/ImageSharp/Formats/AnimatedImageFrameMetadata.cs @@ -30,65 +30,3 @@ internal class AnimatedImageFrameMetadata /// public FrameDisposalMode DisposalMode { get; set; } } - -#pragma warning disable SA1201 // Elements should appear in the correct order -internal enum FrameBlendMode -#pragma warning restore SA1201 // Elements should appear in the correct order -{ - /// - /// Do not blend. Render the current frame on the canvas by overwriting the rectangle covered by the current frame. - /// - Source = 0, - - /// - /// Blend the current frame with the previous frame in the animation sequence within the rectangle covered - /// by the current frame. - /// If the current has any transparent areas, the corresponding areas of the previous frame will be visible - /// through these transparent regions. - /// - Over = 1 -} - -internal enum FrameDisposalMode -{ - /// - /// No disposal specified. - /// The decoder is not required to take any action. - /// - Unspecified = 0, - - /// - /// Do not dispose. The current frame is not disposed of, or in other words, not cleared or altered when moving to - /// the next frame. This means that the next frame is drawn over the current frame, and if the next frame contains - /// transparency, the previous frame will be visible through these transparent areas. - /// - DoNotDispose = 1, - - /// - /// Restore to background color. When transitioning to the next frame, the area occupied by the current frame is - /// filled with the background color specified in the image metadata. - /// This effectively erases the current frame by replacing it with the background color before the next frame is displayed. - /// - RestoreToBackground = 2, - - /// - /// Restore to previous. This method restores the area affected by the current frame to what it was before the - /// current frame was displayed. It essentially "undoes" the current frame, reverting to the state of the image - /// before the frame was displayed, then the next frame is drawn. This is useful for animations where only a small - /// part of the image changes from frame to frame. - /// - RestoreToPrevious = 3 -} - -internal enum FrameColorTableMode -{ - /// - /// The frame uses the shared color table specified by the image metadata. - /// - Global, - - /// - /// The frame uses a color table specified by the frame metadata. - /// - Local -} diff --git a/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs b/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs new file mode 100644 index 000000000..31555afe3 --- /dev/null +++ b/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// A metadata format designed to allow conversion between different image format frames. +/// +public class FormatConnectingFrameMetadata +{ + /// + /// Gets or sets the frame color table. + /// + public ReadOnlyMemory? ColorTable { get; set; } + + /// + /// Gets or sets the frame color table mode. + /// + public FrameColorTableMode ColorTableMode { get; set; } + + /// + /// Gets or sets the duration of the frame. + /// + public TimeSpan Duration { get; set; } + + /// + /// Gets or sets the frame alpha blending mode. + /// + public FrameBlendMode BlendMode { get; set; } + + /// + /// Gets or sets the frame disposal mode. + /// + public FrameDisposalMode DisposalMode { get; set; } +} diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs new file mode 100644 index 000000000..a7030b464 --- /dev/null +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats; + +/// +/// A metadata format designed to allow conversion between different image formats. +/// +public class FormatConnectingMetadata +{ + /// + /// Gets the quality. + /// + /// + /// The value is usually between 1 and 100. Defaults to 100. + /// + public int Quality { get; init; } = 100; + + /// + /// Gets information about the encoded pixel type. + /// + public PixelTypeInfo PixelTypeInfo { get; init; } + + /// + /// Gets the shared color table. + /// + public ReadOnlyMemory? ColorTable { get; init; } + + /// + /// Gets the shared color table mode. + /// + /// + /// Defaults to . + /// + public FrameColorTableMode ColorTableMode { get; init; } + + /// + /// Gets the default background color of the canvas when animating. + /// This color may be used to fill the unused space on the canvas around the frames, + /// as well as the transparent pixels of the first frame. + /// The background color is also used when the disposal mode is . + /// + /// + /// Defaults to . + /// + public Color BackgroundColor { get; init; } = Color.Transparent; + + /// + /// Gets the number of times any animation is repeated. + /// + /// + /// 0 means to repeat indefinitely, count is set as repeat n-1 times. Defaults to 1. + /// + public ushort RepeatCount { get; init; } = 1; + + /// + /// Gets a value indicating whether the root frame is shown as part of the animated sequence. + /// + /// + /// Defaults to . + /// + public bool AnimateRootFrame { get; init; } = true; +} diff --git a/src/ImageSharp/Formats/FrameBlendMode.cs b/src/ImageSharp/Formats/FrameBlendMode.cs new file mode 100644 index 000000000..5785324e5 --- /dev/null +++ b/src/ImageSharp/Formats/FrameBlendMode.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Provides a way to specify how the current frame should be blended with the previous frame in the animation sequence. +/// +public enum FrameBlendMode +{ + /// + /// Do not blend. Render the current frame on the canvas by overwriting the rectangle covered by the current frame. + /// + Source = 0, + + /// + /// Blend the current frame with the previous frame in the animation sequence within the rectangle covered + /// by the current frame. + /// If the current has any transparent areas, the corresponding areas of the previous frame will be visible + /// through these transparent regions. + /// + Over = 1 +} diff --git a/src/ImageSharp/Formats/FrameColorTableMode.cs b/src/ImageSharp/Formats/FrameColorTableMode.cs new file mode 100644 index 000000000..a45b6de0e --- /dev/null +++ b/src/ImageSharp/Formats/FrameColorTableMode.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Provides a way to specify how the color table is used by the frame. +/// +public enum FrameColorTableMode +{ + /// + /// The frame uses the shared color table specified by the image metadata. + /// + Global, + + /// + /// The frame uses a color table specified by the frame metadata. + /// + Local +} diff --git a/src/ImageSharp/Formats/FrameDisposalMode.cs b/src/ImageSharp/Formats/FrameDisposalMode.cs new file mode 100644 index 000000000..196fdb5ad --- /dev/null +++ b/src/ImageSharp/Formats/FrameDisposalMode.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Provides a way to specify how the current frame should be disposed of before rendering the next frame. +/// +public enum FrameDisposalMode +{ + /// + /// No disposal specified. + /// The decoder is not required to take any action. + /// + Unspecified = 0, + + /// + /// Do not dispose. The current frame is not disposed of, or in other words, not cleared or altered when moving to + /// the next frame. This means that the next frame is drawn over the current frame, and if the next frame contains + /// transparency, the previous frame will be visible through these transparent areas. + /// + DoNotDispose = 1, + + /// + /// Restore to background color. When transitioning to the next frame, the area occupied by the current frame is + /// filled with the background color specified in the image metadata. + /// This effectively erases the current frame by replacing it with the background color before the next frame is displayed. + /// + RestoreToBackground = 2, + + /// + /// Restore to previous. This method restores the area affected by the current frame to what it was before the + /// current frame was displayed. It essentially "undoes" the current frame, reverting to the state of the image + /// before the frame was displayed, then the next frame is drawn. This is useful for animations where only a small + /// part of the image changes from frame to frame. + /// + RestoreToPrevious = 3 +} diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 1331edee8..7c83de930 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -67,7 +67,7 @@ public class GifMetadata : IDeepCloneable /// Gets or sets the collection of comments about the graphics, credits, descriptions or any /// other type of non-control and non-graphic data. /// - public IList Comments { get; set; } = new List(); + public IList Comments { get; set; } = []; /// public IDeepCloneable DeepClone() => new GifMetadata(this); diff --git a/src/ImageSharp/Formats/IFormatFrameMetadata.cs b/src/ImageSharp/Formats/IFormatFrameMetadata.cs new file mode 100644 index 000000000..bd7c6c45b --- /dev/null +++ b/src/ImageSharp/Formats/IFormatFrameMetadata.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// An interface that provides metadata for a specific image format frames. +/// +public interface IFormatFrameMetadata : IDeepCloneable +{ + /// + /// Converts the metadata to a instance. + /// + /// The . + FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata(); +} + +/// +/// An interface that provides metadata for a specific image format frames. +/// +/// The metadata type implementing this interface. +public interface IFormatFrameMetadata : IFormatMetadata, IDeepCloneable + where TSelf : class, IFormatFrameMetadata, new() +{ + /// + /// Creates a new instance of the class from the given . + /// + /// The . + /// The . +#pragma warning disable CA1000 // Do not declare static members on generic types + static abstract TSelf FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata); +#pragma warning restore CA1000 // Do not declare static members on generic types +} diff --git a/src/ImageSharp/Formats/IFormatMetadata.cs b/src/ImageSharp/Formats/IFormatMetadata.cs new file mode 100644 index 000000000..f542f92db --- /dev/null +++ b/src/ImageSharp/Formats/IFormatMetadata.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// An interface that provides metadata for a specific image format. +/// +public interface IFormatMetadata : IDeepCloneable +{ + /// + /// Converts the metadata to a instance. + /// + /// The . + FormatConnectingMetadata ToFormatConnectingMetadata(); +} + +/// +/// An interface that provides metadata for a specific image format. +/// +/// The metadata type implementing this interface. +public interface IFormatMetadata : IFormatMetadata, IDeepCloneable + where TSelf : class, IFormatMetadata, new() +{ + /// + /// Creates a new instance of the class from the given . + /// + /// The . + /// The . +#pragma warning disable CA1000 // Do not declare static members on generic types + static abstract TSelf FromFormatConnectingMetadata(FormatConnectingMetadata metadata); +#pragma warning restore CA1000 // Do not declare static members on generic types +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs index faba3d5af..f5666f779 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; /// /// The Huffman encoding specifications. /// -public readonly struct HuffmanSpec +internal readonly struct HuffmanSpec { /// /// Huffman talbe specification for luminance DC. diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index fe1324a86..ce4c0758f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -13,10 +13,7 @@ public class JpegMetadata : IDeepCloneable /// /// Initializes a new instance of the class. /// - public JpegMetadata() - { - this.Comments = new List(); - } + public JpegMetadata() => this.Comments = new List(); /// /// Initializes a new instance of the class. @@ -36,7 +33,7 @@ public class JpegMetadata : IDeepCloneable /// /// /// This value might not be accurate if it was calculated during jpeg decoding - /// with non-complient ITU quantization tables. + /// with non-compliant ITU quantization tables. /// internal int? LuminanceQuality { get; set; } @@ -45,7 +42,7 @@ public class JpegMetadata : IDeepCloneable /// /// /// This value might not be accurate if it was calculated during jpeg decoding - /// with non-complient ITU quantization tables. + /// with non-compliant ITU quantization tables. /// internal int? ChrominanceQuality { get; set; } @@ -69,15 +66,8 @@ public class JpegMetadata : IDeepCloneable return this.LuminanceQuality.Value; } - else - { - if (this.ChrominanceQuality.HasValue) - { - return this.ChrominanceQuality.Value; - } - return Quantization.DefaultQualityFactor; - } + return this.ChrominanceQuality ?? Quantization.DefaultQualityFactor; } } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index d9028dd80..de02e390f 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -77,7 +77,7 @@ public class PngMetadata : IDeepCloneable /// Gets or sets the collection of text data stored within the iTXt, tEXt, and zTXt chunks. /// Used for conveying textual information associated with the image. /// - public IList TextData { get; set; } = new List(); + public IList TextData { get; set; } = []; /// /// Gets or sets the number of times to loop this APNG. 0 indicates infinite looping. @@ -96,7 +96,7 @@ public class PngMetadata : IDeepCloneable { // Should the conversion be from a format that uses a 24bit palette entries (gif) // we need to clone and adjust the color table to allow for transparency. - Color[]? colorTable = metadata.ColorTable.HasValue ? metadata.ColorTable.Value.ToArray() : null; + Color[]? colorTable = metadata.ColorTable?.ToArray(); if (colorTable != null) { for (int i = 0; i < colorTable.Length; i++) diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs index 9ac2c308c..86c3d51e9 100644 --- a/src/ImageSharp/PixelFormats/PixelColorType.cs +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -9,6 +9,11 @@ namespace SixLabors.ImageSharp.PixelFormats; [Flags] public enum PixelColorType { + /// + /// No color type. + /// + None = 0, + /// /// Represents the Red component of the color. /// @@ -42,5 +47,60 @@ public enum PixelColorType /// /// Indicates that the color is in BGR (Blue, Green, Red) format. /// - BGR = Blue | Green | Red | (1 << 6) + BGR = Blue | Green | Red | (1 << 6), + + /// + /// Represents the Luminance component in YCbCr. + /// + Luminance = 1 << 7, + + /// + /// Represents the Chrominance Blue component in YCbCr. + /// + ChrominanceBlue = 1 << 8, + + /// + /// Represents the Chrominance Red component in YCbCr. + /// + ChrominanceRed = 1 << 9, + + /// + /// Indicates that the color is in YCbCr (Luminance, Chrominance Blue, Chrominance Red) format. + /// + YCbCr = Luminance | ChrominanceBlue | ChrominanceRed, + + /// + /// Represents the Cyan component in CMYK. + /// + Cyan = 1 << 10, + + /// + /// Represents the Magenta component in CMYK. + /// + Magenta = 1 << 11, + + /// + /// Represents the Yellow component in CMYK. + /// + Yellow = 1 << 12, + + /// + /// Represents the Key (black) component in CMYK and YCCK. + /// + Key = 1 << 13, + + /// + /// Indicates that the color is in CMYK (Cyan, Magenta, Yellow, Key) format. + /// + CMYK = Cyan | Magenta | Yellow | Key, + + /// + /// Indicates that the color is in YCCK (Luminance, Chrominance Blue, Chrominance Red, Key) format. + /// + YCCK = Luminance | ChrominanceBlue | ChrominanceRed | Key, + + /// + /// Indicates that the color is of a type not specified in this enum. + /// + Other = 1 << 14 } diff --git a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs index a76105414..fca371ca9 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -44,7 +44,7 @@ public readonly struct PixelComponentInfo { if (precision.Length != count || precision.Length > 16) { - throw new ArgumentException($"Count must match the length of precision array and cannot exceed 16."); + throw new ArgumentOutOfRangeException(nameof(count), $"Count {count} must match the length of precision array and cannot exceed 16."); } long precisionData1 = 0; @@ -55,7 +55,7 @@ public readonly struct PixelComponentInfo int p = precision[i]; if (p is < 0 or > 255) { - throw new ArgumentException("Precision must be between 0 and 255."); + throw new ArgumentOutOfRangeException(nameof(precision), $"Precision {precision.Length} must be between 0 and 255."); } if (i < 8) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs new file mode 100644 index 000000000..215fe7b87 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -0,0 +1,255 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +public class PixelColorTypeTests +{ + [Fact] + public void PixelColorType_RedFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Red; + Assert.True(colorType.HasFlag(PixelColorType.Red)); + } + + [Fact] + public void PixelColorType_GreenFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Green; + Assert.True(colorType.HasFlag(PixelColorType.Green)); + } + + [Fact] + public void PixelColorType_BlueFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Blue; + Assert.True(colorType.HasFlag(PixelColorType.Blue)); + } + + [Fact] + public void PixelColorType_AlphaFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Alpha; + Assert.True(colorType.HasFlag(PixelColorType.Alpha)); + } + + [Fact] + public void PixelColorType_GrayscaleFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Grayscale; + Assert.True(colorType.HasFlag(PixelColorType.Grayscale)); + } + + [Fact] + public void PixelColorType_RGBFlags_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.RGB; + Assert.True(colorType.HasFlag(PixelColorType.Red)); + Assert.True(colorType.HasFlag(PixelColorType.Green)); + Assert.True(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.BGR)); + } + + [Fact] + public void PixelColorType_BGRFlags_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.BGR; + Assert.True(colorType.HasFlag(PixelColorType.Blue)); + Assert.True(colorType.HasFlag(PixelColorType.Green)); + Assert.True(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.RGB)); + } + + [Fact] + public void PixelColorType_LuminanceFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Luminance; + Assert.True(colorType.HasFlag(PixelColorType.Luminance)); + } + + [Fact] + public void PixelColorType_ChrominanceBlueFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.ChrominanceBlue; + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + } + + [Fact] + public void PixelColorType_ChrominanceRedFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.ChrominanceRed; + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceRed)); + } + + [Fact] + public void PixelColorType_YCbCrFlags_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.YCbCr; + Assert.True(colorType.HasFlag(PixelColorType.Luminance)); + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceRed)); + } + + [Fact] + public void PixelColorType_CyanFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Cyan; + Assert.True(colorType.HasFlag(PixelColorType.Cyan)); + } + + [Fact] + public void PixelColorType_MagentaFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Magenta; + Assert.True(colorType.HasFlag(PixelColorType.Magenta)); + } + + [Fact] + public void PixelColorType_YellowFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Yellow; + Assert.True(colorType.HasFlag(PixelColorType.Yellow)); + } + + [Fact] + public void PixelColorType_KeyFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Key; + Assert.True(colorType.HasFlag(PixelColorType.Key)); + } + + [Fact] + public void PixelColorType_CMYKFlags_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.CMYK; + Assert.True(colorType.HasFlag(PixelColorType.Cyan)); + Assert.True(colorType.HasFlag(PixelColorType.Magenta)); + Assert.True(colorType.HasFlag(PixelColorType.Yellow)); + Assert.True(colorType.HasFlag(PixelColorType.Key)); + } + + [Fact] + public void PixelColorType_YCCKFlags_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.YCCK; + Assert.True(colorType.HasFlag(PixelColorType.Luminance)); + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.True(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.True(colorType.HasFlag(PixelColorType.Key)); + } + + [Fact] + public void PixelColorType_Other_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Other; + Assert.True(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_None_ShouldBeZero() + { + const PixelColorType colorType = PixelColorType.None; + Assert.Equal(0, (int)colorType); + } + + [Fact] + public void PixelColorType_RGB_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.RGB; + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_BGR_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.BGR; + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_YCbCr_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.YCbCr; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_CMYK_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.CMYK; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_YCCK_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.YCCK; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_Other_ShouldNotContainPreviousFlags() + { + const PixelColorType colorType = PixelColorType.Other; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.RGB)); + Assert.False(colorType.HasFlag(PixelColorType.BGR)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.CMYK)); + Assert.False(colorType.HasFlag(PixelColorType.YCCK)); + } +} From a971082a0c02001e4b5fc94cad1d9d31a6a51371 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 21:43:19 +1000 Subject: [PATCH 148/220] Sketch out Bmp implementation --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 25 ++++++++++++++++--- .../Formats/IFormatFrameMetadata.cs | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index a2ed1d21d..f381249d8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp; @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Provides Bmp specific metadata information for the image. /// -public class BmpMetadata : IDeepCloneable +public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -36,7 +36,26 @@ public class BmpMetadata : IDeepCloneable public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; /// - public IDeepCloneable DeepClone() => new BmpMetadata(this); + public static BmpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + => throw new NotImplementedException(); + + /// + public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + => throw new NotImplementedException(); + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => throw new NotImplementedException(); + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => throw new NotImplementedException(); + + /// + public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); + + /// + BmpMetadata IDeepCloneable.DeepClone() => new(this); // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/IFormatFrameMetadata.cs b/src/ImageSharp/Formats/IFormatFrameMetadata.cs index bd7c6c45b..4c5321073 100644 --- a/src/ImageSharp/Formats/IFormatFrameMetadata.cs +++ b/src/ImageSharp/Formats/IFormatFrameMetadata.cs @@ -19,7 +19,7 @@ public interface IFormatFrameMetadata : IDeepCloneable /// An interface that provides metadata for a specific image format frames. /// /// The metadata type implementing this interface. -public interface IFormatFrameMetadata : IFormatMetadata, IDeepCloneable +public interface IFormatFrameMetadata : IFormatFrameMetadata, IDeepCloneable where TSelf : class, IFormatFrameMetadata, new() { /// From d52815bfd0730ffb4dd1aea7acc12e21a8bc8092 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 23:05:49 +1000 Subject: [PATCH 149/220] Wire up BmpMetadata proper --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 96 +++++++++++++++++-- .../Formats/FormatConnectingMetadata.cs | 2 +- src/ImageSharp/PixelFormats/PixelColorType.cs | 7 +- .../PixelFormats/PixelComponentInfo.cs | 13 ++- .../PixelFormats/PixelColorTypeTests.cs | 37 +++++++ 5 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index f381249d8..2f03b9c6f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -1,6 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + +// TODO: Add color table information. namespace SixLabors.ImageSharp.Formats.Bmp; /// @@ -37,25 +40,106 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata public static BmpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) - => throw new NotImplementedException(); + { + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + if (bpp == 1) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel1 }; + } + + if (bpp == 2) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel2 }; + } + + if (bpp <= 4) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel4 }; + } + + if (bpp <= 8) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }; + } + + if (bpp <= 16) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 }; + } + + if (bpp <= 24) + { + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 }; + } + + return new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 }; + } /// public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) - => throw new NotImplementedException(); + => new(); /// public FormatConnectingMetadata ToFormatConnectingMetadata() - => throw new NotImplementedException(); + { + int bpp = (int)this.BitsPerPixel; + + PixelAlphaRepresentation alpha = this.InfoHeaderType switch + { + BmpInfoHeaderType.WinVersion2 or + BmpInfoHeaderType.Os2Version2Short or + BmpInfoHeaderType.WinVersion3 or + BmpInfoHeaderType.AdobeVersion3 or + BmpInfoHeaderType.Os2Version2 => PixelAlphaRepresentation.None, + BmpInfoHeaderType.AdobeVersion3WithAlpha or + BmpInfoHeaderType.WinVersion4 or + BmpInfoHeaderType.WinVersion5 or + _ => bpp < 32 ? PixelAlphaRepresentation.None : PixelAlphaRepresentation.Unassociated + }; + + PixelComponentInfo info = this.BitsPerPixel switch + { + BmpBitsPerPixel.Pixel1 => PixelComponentInfo.Create(1, bpp, 1), + BmpBitsPerPixel.Pixel2 => PixelComponentInfo.Create(1, bpp, 2), + BmpBitsPerPixel.Pixel4 => PixelComponentInfo.Create(1, bpp, 4), + BmpBitsPerPixel.Pixel8 => PixelComponentInfo.Create(1, bpp, 8), + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + BmpBitsPerPixel.Pixel16 => PixelComponentInfo.Create(3, bpp, 5, 6, 5), + BmpBitsPerPixel.Pixel24 => PixelComponentInfo.Create(3, bpp, 8, 8, 8), + BmpBitsPerPixel.Pixel32 or _ => PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8), + }; + + PixelColorType color = this.BitsPerPixel switch + { + BmpBitsPerPixel.Pixel1 or + BmpBitsPerPixel.Pixel2 or + BmpBitsPerPixel.Pixel4 or + BmpBitsPerPixel.Pixel8 => PixelColorType.Indexed, + BmpBitsPerPixel.Pixel16 or + BmpBitsPerPixel.Pixel24 => PixelColorType.RGB, + BmpBitsPerPixel.Pixel32 or _ => PixelColorType.RGB | PixelColorType.Alpha, + }; + + return new() + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + } + }; + } /// public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() - => throw new NotImplementedException(); + => new(); /// public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); /// BmpMetadata IDeepCloneable.DeepClone() => new(this); - - // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs index a7030b464..2acbe9699 100644 --- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -34,7 +34,7 @@ public class FormatConnectingMetadata /// /// Defaults to . /// - public FrameColorTableMode ColorTableMode { get; init; } + public FrameColorTableMode ColorTableMode { get; init; } = FrameColorTableMode.Global; /// /// Gets the default background color of the canvas when animating. diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs index 86c3d51e9..52d4df73a 100644 --- a/src/ImageSharp/PixelFormats/PixelColorType.cs +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -99,8 +99,13 @@ public enum PixelColorType /// YCCK = Luminance | ChrominanceBlue | ChrominanceRed | Key, + /// + /// Indicates that the color is indexed using a palette. + /// + Indexed = 1 << 14, + /// /// Indicates that the color is of a type not specified in this enum. /// - Other = 1 << 14 + Other = 1 << 15 } diff --git a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs index fca371ca9..64b233f35 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -41,6 +41,17 @@ public readonly struct PixelComponentInfo /// The component precision and index cannot exceed the component range. public static PixelComponentInfo Create(int count, params int[] precision) where TPixel : unmanaged, IPixel + => Create(count, Unsafe.SizeOf() * 8, precision); + + /// + /// Creates a new instance. + /// + /// The number of components within the pixel format. + /// The number of bits per pixel. + /// The precision in bits of each component. + /// The . + /// The component precision and index cannot exceed the component range. + public static PixelComponentInfo Create(int count, int bitsPerPixel, params int[] precision) { if (precision.Length != count || precision.Length > 16) { @@ -70,7 +81,7 @@ public readonly struct PixelComponentInfo sum += p; } - return new PixelComponentInfo(count, (Unsafe.SizeOf() * 8) - sum, precisionData1, precisionData2); + return new PixelComponentInfo(count, bitsPerPixel - sum, precisionData1, precisionData2); } /// diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs index 215fe7b87..09e84d53b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -140,6 +140,13 @@ public class PixelColorTypeTests Assert.True(colorType.HasFlag(PixelColorType.Key)); } + [Fact] + public void PixelColorType_Indexed_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.True(colorType.HasFlag(PixelColorType.Indexed)); + } + [Fact] public void PixelColorType_Other_ShouldBeSet() { @@ -166,6 +173,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -181,6 +189,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -197,6 +206,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -212,6 +222,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Luminance)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -227,6 +238,31 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + + [Fact] + public void PixelColorType_Indexed_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.RGB)); + Assert.False(colorType.HasFlag(PixelColorType.BGR)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.CMYK)); + Assert.False(colorType.HasFlag(PixelColorType.YCCK)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -251,5 +287,6 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Key)); Assert.False(colorType.HasFlag(PixelColorType.CMYK)); Assert.False(colorType.HasFlag(PixelColorType.YCCK)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); } } From 2752a450d8be7856a0a0d1528d675752cff76d56 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 14:05:14 +1000 Subject: [PATCH 150/220] Implement up GifMetadata --- .../Formats/Gif/GifColorTableMode.cs | 20 ------ src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 8 +-- src/ImageSharp/Formats/Gif/GifEncoder.cs | 2 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 12 ++-- .../Formats/Gif/GifFrameMetadata.cs | 4 +- src/ImageSharp/Formats/Gif/GifMetadata.cs | 64 ++++++++++++++++-- .../Formats/Gif/MetadataExtensions.cs | 6 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 4 +- src/ImageSharp/PixelFormats/PixelColorType.cs | 40 +++++------ .../PixelFormats/PixelComponentInfo.cs | 2 +- .../PixelFormats/PixelImplementations/L16.cs | 2 +- .../PixelFormats/PixelImplementations/L8.cs | 2 +- .../PixelFormats/PixelImplementations/La16.cs | 2 +- .../PixelFormats/PixelImplementations/La32.cs | 2 +- .../Formats/Gif/GifEncoderTests.cs | 15 +++-- .../Formats/Gif/GifMetadataTests.cs | 10 +-- .../ImageFrameCollectionTests.NonGeneric.cs | 3 +- .../ImageSharp.Tests/PixelFormats/L16Tests.cs | 2 +- .../ImageSharp.Tests/PixelFormats/L8Tests.cs | 2 +- .../PixelFormats/La16Tests.cs | 2 +- .../PixelFormats/La32Tests.cs | 2 +- .../PixelFormats/PixelColorTypeTests.cs | 66 ++++++++++--------- 22 files changed, 154 insertions(+), 118 deletions(-) delete mode 100644 src/ImageSharp/Formats/Gif/GifColorTableMode.cs diff --git a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs b/src/ImageSharp/Formats/Gif/GifColorTableMode.cs deleted file mode 100644 index 0f65f4602..000000000 --- a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Gif; - -/// -/// Provides enumeration for the available color table modes. -/// -public enum GifColorTableMode -{ - /// - /// A single color table is calculated from the first frame and reused for subsequent frames. - /// - Global, - - /// - /// A unique color table is calculated for each frame. - /// - Local -} diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index d64792eba..a67ee8f98 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -696,14 +696,14 @@ internal sealed class GifDecoderCore : IImageDecoderInternals && this.logicalScreenDescriptor.GlobalColorTableSize > 0) { GifFrameMetadata gifMeta = metadata.GetGifMetadata(); - gifMeta.ColorTableMode = GifColorTableMode.Global; + gifMeta.ColorTableMode = FrameColorTableMode.Global; } if (this.imageDescriptor.LocalColorTableFlag && this.imageDescriptor.LocalColorTableSize > 0) { GifFrameMetadata gifMeta = metadata.GetGifMetadata(); - gifMeta.ColorTableMode = GifColorTableMode.Local; + gifMeta.ColorTableMode = FrameColorTableMode.Local; Color[] colorTable = new Color[this.imageDescriptor.LocalColorTableSize]; ReadOnlySpan rgbTable = MemoryMarshal.Cast(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]); @@ -766,8 +766,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals this.metadata = meta; this.gifMetadata = meta.GetGifMetadata(); this.gifMetadata.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag - ? GifColorTableMode.Global - : GifColorTableMode.Local; + ? FrameColorTableMode.Global + : FrameColorTableMode.Local; if (this.logicalScreenDescriptor.GlobalColorTableFlag) { diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index ab05548ac..6cb8f9d8c 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -11,7 +11,7 @@ public sealed class GifEncoder : QuantizingImageEncoder /// /// Gets the color table mode: Global or local. /// - public GifColorTableMode? ColorTableMode { get; init; } + public FrameColorTableMode? ColorTableMode { get; init; } /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 95429e606..70afe12e1 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -49,7 +49,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals /// /// The color table mode: Global or local. /// - private GifColorTableMode? colorTableMode; + private FrameColorTableMode? colorTableMode; /// /// The pixel sampling strategy for global quantization. @@ -87,7 +87,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals GifMetadata gifMetadata = GetGifMetadata(image); this.colorTableMode ??= gifMetadata.ColorTableMode; - bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; + bool useGlobalTable = this.colorTableMode == FrameColorTableMode.Global; // Quantize the first image frame returning a palette. IndexedImageFrame? quantized = null; @@ -99,7 +99,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals if (this.quantizer is null) { // Is this a gif with color information. If so use that, otherwise use octree. - if (gifMetadata.ColorTableMode == GifColorTableMode.Global && gifMetadata.GlobalColorTable?.Length > 0) + if (gifMetadata.ColorTableMode == FrameColorTableMode.Global && gifMetadata.GlobalColorTable?.Length > 0) { // We avoid dithering by default to preserve the original colors. int transparencyIndex = GetTransparentIndex(quantized, frameMetadata); @@ -221,7 +221,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals metadata = GifFrameMetadata.FromAnimatedMetadata(ani); } - if (metadata?.ColorTableMode == GifColorTableMode.Global && transparencyIndex > -1) + if (metadata?.ColorTableMode == FrameColorTableMode.Global && transparencyIndex > -1) { metadata.HasTransparency = true; metadata.TransparencyIndex = ClampIndex(transparencyIndex); @@ -258,7 +258,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals ImageFrame currentFrame = image.Frames[i]; ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; GifFrameMetadata gifMetadata = GetGifFrameMetadata(currentFrame, globalTransparencyIndex); - bool useLocal = this.colorTableMode == GifColorTableMode.Local || (gifMetadata.ColorTableMode == GifColorTableMode.Local); + bool useLocal = this.colorTableMode == FrameColorTableMode.Local || (gifMetadata.ColorTableMode == FrameColorTableMode.Local); if (!useLocal && !hasPaletteQuantizer && i > 0) { @@ -301,7 +301,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals Buffer2D indices = ((IPixelSource)quantized).PixelBuffer; Rectangle interest = indices.FullRectangle(); - bool useLocal = this.colorTableMode == GifColorTableMode.Local || (metadata.ColorTableMode == GifColorTableMode.Local); + bool useLocal = this.colorTableMode == FrameColorTableMode.Local || (metadata.ColorTableMode == FrameColorTableMode.Local); int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length); this.WriteImageDescriptor(interest, useLocal, bitDepth, stream); diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs index 6598def2a..3f8563706 100644 --- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs @@ -40,7 +40,7 @@ public class GifFrameMetadata : IDeepCloneable /// /// Gets or sets the color table mode. /// - public GifColorTableMode ColorTableMode { get; set; } + public FrameColorTableMode ColorTableMode { get; set; } /// /// Gets or sets the local color table, if any. @@ -101,7 +101,7 @@ public class GifFrameMetadata : IDeepCloneable return new() { LocalColorTable = metadata.ColorTable, - ColorTableMode = metadata.ColorTableMode == FrameColorTableMode.Global ? GifColorTableMode.Global : GifColorTableMode.Local, + ColorTableMode = metadata.ColorTableMode, FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10), DisposalMethod = GetMode(metadata.DisposalMode), HasTransparency = hasTransparency, diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 7c83de930..43935504b 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Provides Gif specific metadata information for the image. /// -public class GifMetadata : IDeepCloneable +public class GifMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ public class GifMetadata : IDeepCloneable /// /// Gets or sets the color table mode. /// - public GifColorTableMode ColorTableMode { get; set; } + public FrameColorTableMode ColorTableMode { get; set; } /// /// Gets or sets the global color table, if any. @@ -69,9 +69,6 @@ public class GifMetadata : IDeepCloneable /// public IList Comments { get; set; } = []; - /// - public IDeepCloneable DeepClone() => new GifMetadata(this); - internal static GifMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) { int index = 0; @@ -92,9 +89,64 @@ public class GifMetadata : IDeepCloneable return new() { GlobalColorTable = metadata.ColorTable, - ColorTableMode = metadata.ColorTableMode == FrameColorTableMode.Global ? GifColorTableMode.Global : GifColorTableMode.Local, + ColorTableMode = metadata.ColorTableMode, + RepeatCount = metadata.RepeatCount, + BackgroundColorIndex = (byte)Numerics.Clamp(index, 0, 255), + }; + } + + /// + public static GifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + int index = 0; + Color background = metadata.BackgroundColor; + if (metadata.ColorTable.HasValue) + { + ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; + for (int i = 0; i < colorTable.Length; i++) + { + if (background == colorTable[i]) + { + index = i; + break; + } + } + } + + return new() + { + GlobalColorTable = metadata.ColorTable, + ColorTableMode = metadata.ColorTableMode, RepeatCount = metadata.RepeatCount, BackgroundColorIndex = (byte)Numerics.Clamp(index, 0, 255), }; } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + Color color = this.GlobalColorTable.HasValue && this.GlobalColorTable.Value.Span.Length > this.BackgroundColorIndex + ? this.GlobalColorTable.Value.Span[this.BackgroundColorIndex] + : Color.Transparent; + + return new() + { + AnimateRootFrame = true, + BackgroundColor = color, + ColorTable = this.GlobalColorTable, + ColorTableMode = this.ColorTableMode, + PixelTypeInfo = new PixelTypeInfo(24) + { + ColorType = PixelColorType.Indexed, + ComponentInfo = PixelComponentInfo.Create(3, 24, 8, 8, 8), + }, + RepeatCount = this.RepeatCount, + }; + } + + /// + public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); + + /// + GifMetadata IDeepCloneable.DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index ad06462e7..d4650403c 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -70,7 +70,7 @@ public static partial class MetadataExtensions return new() { ColorTable = source.GlobalColorTable, - ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local, + ColorTableMode = source.ColorTableMode, RepeatCount = source.RepeatCount, BackgroundColor = background, }; @@ -83,12 +83,12 @@ public static partial class MetadataExtensions bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); // If the color table is global and frame has no transparency. Consider it 'Source' also. - blendSource |= source.ColorTableMode == GifColorTableMode.Global && !source.HasTransparency; + blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency; return new() { ColorTable = source.LocalColorTable, - ColorTableMode = source.ColorTableMode == GifColorTableMode.Global ? FrameColorTableMode.Global : FrameColorTableMode.Local, + ColorTableMode = source.ColorTableMode, Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), DisposalMode = GetMode(source.DisposalMethod), BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 6e8224f01..36ce136f4 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1680,14 +1680,14 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { return info.ColorType switch { - PixelColorType.Grayscale => PngColorType.Grayscale, + PixelColorType.Luminance => PngColorType.Grayscale, _ => PngColorType.Rgb, }; } return info.ColorType switch { - PixelColorType.Grayscale | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha, + PixelColorType.Luminance | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha, _ => PngColorType.RgbWithAlpha, }; } diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs index 52d4df73a..afbe6a4d3 100644 --- a/src/ImageSharp/PixelFormats/PixelColorType.cs +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -35,34 +35,39 @@ public enum PixelColorType Alpha = 1 << 3, /// - /// Indicates that the color is in grayscale. + /// Represents the Exponent component used in formats like R9G9B9E5. /// - Grayscale = 1 << 4, + Exponent = 1 << 4, /// - /// Indicates that the color is in RGB (Red, Green, Blue) format. + /// Indicates that the color is in luminance (grayscale) format. /// - RGB = Red | Green | Blue | (1 << 5), + Luminance = 1 << 5, /// - /// Indicates that the color is in BGR (Blue, Green, Red) format. + /// Indicates that the color is indexed using a palette. + /// + Indexed = 1 << 6, + + /// + /// Indicates that the color is in RGB (Red, Green, Blue) format. /// - BGR = Blue | Green | Red | (1 << 6), + RGB = Red | Green | Blue | (1 << 7), /// - /// Represents the Luminance component in YCbCr. + /// Indicates that the color is in BGR (Blue, Green, Red) format. /// - Luminance = 1 << 7, + BGR = Blue | Green | Red | (1 << 8), /// /// Represents the Chrominance Blue component in YCbCr. /// - ChrominanceBlue = 1 << 8, + ChrominanceBlue = 1 << 9, /// /// Represents the Chrominance Red component in YCbCr. /// - ChrominanceRed = 1 << 9, + ChrominanceRed = 1 << 10, /// /// Indicates that the color is in YCbCr (Luminance, Chrominance Blue, Chrominance Red) format. @@ -72,22 +77,22 @@ public enum PixelColorType /// /// Represents the Cyan component in CMYK. /// - Cyan = 1 << 10, + Cyan = 1 << 11, /// /// Represents the Magenta component in CMYK. /// - Magenta = 1 << 11, + Magenta = 1 << 12, /// /// Represents the Yellow component in CMYK. /// - Yellow = 1 << 12, + Yellow = 1 << 13, /// /// Represents the Key (black) component in CMYK and YCCK. /// - Key = 1 << 13, + Key = 1 << 14, /// /// Indicates that the color is in CMYK (Cyan, Magenta, Yellow, Key) format. @@ -99,13 +104,8 @@ public enum PixelColorType /// YCCK = Luminance | ChrominanceBlue | ChrominanceRed | Key, - /// - /// Indicates that the color is indexed using a palette. - /// - Indexed = 1 << 14, - /// /// Indicates that the color is of a type not specified in this enum. /// - Other = 1 << 15 + Other = 1 << 16 } diff --git a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs index 64b233f35..1444b344b 100644 --- a/src/ImageSharp/PixelFormats/PixelComponentInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -53,7 +53,7 @@ public readonly struct PixelComponentInfo /// The component precision and index cannot exceed the component range. public static PixelComponentInfo Create(int count, int bitsPerPixel, params int[] precision) { - if (precision.Length != count || precision.Length > 16) + if (precision.Length < count || precision.Length > 16) { throw new ArgumentOutOfRangeException(nameof(count), $"Count {count} must match the length of precision array and cannot exceed 16."); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 2b5241b0b..64a22060c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -71,7 +71,7 @@ public partial struct L16 : IPixel, IPackedVector public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( PixelComponentInfo.Create(1, 16), - PixelColorType.Grayscale, + PixelColorType.Luminance, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 5d733bdbb..cf8646cfa 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -73,7 +73,7 @@ public partial struct L8 : IPixel, IPackedVector public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( PixelComponentInfo.Create(1, 8), - PixelColorType.Grayscale, + PixelColorType.Luminance, PixelAlphaRepresentation.None); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 69ca66218..026d0c299 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -100,7 +100,7 @@ public partial struct La16 : IPixel, IPackedVector public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( PixelComponentInfo.Create(2, 8, 8), - PixelColorType.Grayscale | PixelColorType.Alpha, + PixelColorType.Luminance | PixelColorType.Alpha, PixelAlphaRepresentation.Unassociated); /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index 1886ef39a..0ddcf16a1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -97,7 +97,7 @@ public partial struct La32 : IPixel, IPackedVector public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( PixelComponentInfo.Create(2, 16, 16), - PixelColorType.Grayscale | PixelColorType.Alpha, + PixelColorType.Luminance | PixelColorType.Alpha, PixelAlphaRepresentation.Unassociated); /// diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index a7e16f773..767141f56 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Webp; @@ -113,7 +114,7 @@ public class GifEncoderTests using Image image = provider.GetImage(); GifEncoder encoder = new() { - ColorTableMode = GifColorTableMode.Global, + ColorTableMode = FrameColorTableMode.Global, Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; @@ -122,7 +123,7 @@ public class GifEncoderTests encoder = new() { - ColorTableMode = GifColorTableMode.Local, + ColorTableMode = FrameColorTableMode.Local, Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }), }; @@ -148,7 +149,7 @@ public class GifEncoderTests GifEncoder encoder = new() { - ColorTableMode = GifColorTableMode.Global, + ColorTableMode = FrameColorTableMode.Global, PixelSamplingStrategy = new DefaultPixelSamplingStrategy(maxPixels, scanRatio) }; @@ -175,10 +176,10 @@ public class GifEncoderTests Image image = Image.Load(inStream); GifMetadata metaData = image.Metadata.GetGifMetadata(); GifFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetGifMetadata(); - GifColorTableMode colorMode = metaData.ColorTableMode; + FrameColorTableMode colorMode = metaData.ColorTableMode; int maxColors; - if (colorMode == GifColorTableMode.Global) + if (colorMode == FrameColorTableMode.Global) { maxColors = metaData.GlobalColorTable.Value.Length; } @@ -204,7 +205,7 @@ public class GifEncoderTests // Gifiddle and Cyotek GifInfo say this image has 64 colors. colorMode = cloneMetadata.ColorTableMode; - if (colorMode == GifColorTableMode.Global) + if (colorMode == FrameColorTableMode.Global) { maxColors = metaData.GlobalColorTable.Value.Length; } @@ -220,7 +221,7 @@ public class GifEncoderTests GifFrameMetadata iMeta = image.Frames[i].Metadata.GetGifMetadata(); GifFrameMetadata cMeta = clone.Frames[i].Metadata.GetGifMetadata(); - if (iMeta.ColorTableMode == GifColorTableMode.Local) + if (iMeta.ColorTableMode == FrameColorTableMode.Local) { Assert.Equal(iMeta.LocalColorTable.Value.Length, cMeta.LocalColorTable.Value.Length); } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index fb4445cda..c7babe932 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -33,7 +33,7 @@ public class GifMetadataTests GifMetadata meta = new() { RepeatCount = 1, - ColorTableMode = GifColorTableMode.Global, + ColorTableMode = FrameColorTableMode.Global, GlobalColorTable = new[] { Color.Black, Color.White }, Comments = new List { "Foo" } }; @@ -41,7 +41,7 @@ public class GifMetadataTests GifMetadata clone = (GifMetadata)meta.DeepClone(); clone.RepeatCount = 2; - clone.ColorTableMode = GifColorTableMode.Local; + clone.ColorTableMode = FrameColorTableMode.Local; clone.GlobalColorTable = new[] { Color.Black }; Assert.False(meta.RepeatCount.Equals(clone.RepeatCount)); @@ -183,11 +183,11 @@ public class GifMetadataTests } [Theory] - [InlineData(TestImages.Gif.Cheers, 93, GifColorTableMode.Global, 256, 4, GifDisposalMethod.NotDispose)] + [InlineData(TestImages.Gif.Cheers, 93, FrameColorTableMode.Global, 256, 4, GifDisposalMethod.NotDispose)] public void Identify_Frames( string imagePath, int framesCount, - GifColorTableMode colorTableMode, + FrameColorTableMode colorTableMode, int globalColorTableLength, int frameDelay, GifDisposalMethod disposalMethod) @@ -206,7 +206,7 @@ public class GifMetadataTests Assert.Equal(colorTableMode, gifFrameMetadata.ColorTableMode); - if (colorTableMode == GifColorTableMode.Global) + if (colorTableMode == FrameColorTableMode.Global) { Assert.Equal(globalColorTableLength, gifMetadata.GlobalColorTable.Value.Length); } diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index a42dcc481..572c5b2eb 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; @@ -315,7 +316,7 @@ public abstract partial class ImageFrameCollectionTests Assert.Equal(aData.DisposalMethod, bData.DisposalMethod); Assert.Equal(aData.FrameDelay, bData.FrameDelay); - if (aData.ColorTableMode == GifColorTableMode.Local && bData.ColorTableMode == GifColorTableMode.Local) + if (aData.ColorTableMode == FrameColorTableMode.Local && bData.ColorTableMode == FrameColorTableMode.Local) { Assert.Equal(aData.LocalColorTable.Value.Length, bData.LocalColorTable.Value.Length); } diff --git a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 2ddf1accb..7f0a4217c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -161,7 +161,7 @@ public class L16Tests PixelTypeInfo info = L16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelColorType.Grayscale, info.ColorType); + Assert.Equal(PixelColorType.Luminance, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index 40c746cf2..1ca865ef4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -245,7 +245,7 @@ public class L8Tests PixelTypeInfo info = L8.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); - Assert.Equal(PixelColorType.Grayscale, info.ColorType); + Assert.Equal(PixelColorType.Luminance, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(1, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs index a18b31f6b..f6cbfc442 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -248,7 +248,7 @@ public class La16Tests PixelTypeInfo info = La16.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelColorType.Grayscale | PixelColorType.Alpha, info.ColorType); + Assert.Equal(PixelColorType.Luminance | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs index 3c702419d..fd5556d3b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -167,7 +167,7 @@ public class La32Tests PixelTypeInfo info = La32.GetPixelTypeInfo(); Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); - Assert.Equal(PixelColorType.Grayscale | PixelColorType.Alpha, info.ColorType); + Assert.Equal(PixelColorType.Luminance | PixelColorType.Alpha, info.ColorType); PixelComponentInfo componentInfo = info.ComponentInfo.Value; Assert.Equal(2, componentInfo.ComponentCount); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs index 09e84d53b..c42713005 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -36,10 +36,24 @@ public class PixelColorTypeTests } [Fact] - public void PixelColorType_GrayscaleFlag_ShouldBeSet() + public void PixelColorType_Exponent_ShouldBeSet() { - const PixelColorType colorType = PixelColorType.Grayscale; - Assert.True(colorType.HasFlag(PixelColorType.Grayscale)); + const PixelColorType colorType = PixelColorType.Exponent; + Assert.True(colorType.HasFlag(PixelColorType.Exponent)); + } + + [Fact] + public void PixelColorType_LuminanceFlag_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Luminance; + Assert.True(colorType.HasFlag(PixelColorType.Luminance)); + } + + [Fact] + public void PixelColorType_Indexed_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.True(colorType.HasFlag(PixelColorType.Indexed)); } [Fact] @@ -62,13 +76,6 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.RGB)); } - [Fact] - public void PixelColorType_LuminanceFlag_ShouldBeSet() - { - const PixelColorType colorType = PixelColorType.Luminance; - Assert.True(colorType.HasFlag(PixelColorType.Luminance)); - } - [Fact] public void PixelColorType_ChrominanceBlueFlag_ShouldBeSet() { @@ -140,13 +147,6 @@ public class PixelColorTypeTests Assert.True(colorType.HasFlag(PixelColorType.Key)); } - [Fact] - public void PixelColorType_Indexed_ShouldBeSet() - { - const PixelColorType colorType = PixelColorType.Indexed; - Assert.True(colorType.HasFlag(PixelColorType.Indexed)); - } - [Fact] public void PixelColorType_Other_ShouldBeSet() { @@ -165,15 +165,16 @@ public class PixelColorTypeTests public void PixelColorType_RGB_ShouldNotContainOtherFlags() { const PixelColorType colorType = PixelColorType.RGB; - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -181,15 +182,16 @@ public class PixelColorTypeTests public void PixelColorType_BGR_ShouldNotContainOtherFlags() { const PixelColorType colorType = PixelColorType.BGR; - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -201,12 +203,12 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Green)); Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); Assert.False(colorType.HasFlag(PixelColorType.Key)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -218,11 +220,11 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Green)); Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -234,11 +236,11 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Green)); Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); Assert.False(colorType.HasFlag(PixelColorType.Yellow)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Other)); } @@ -250,10 +252,10 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Green)); Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); Assert.False(colorType.HasFlag(PixelColorType.RGB)); Assert.False(colorType.HasFlag(PixelColorType.BGR)); - Assert.False(colorType.HasFlag(PixelColorType.Luminance)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); @@ -274,10 +276,11 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Green)); Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); - Assert.False(colorType.HasFlag(PixelColorType.Grayscale)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.RGB)); Assert.False(colorType.HasFlag(PixelColorType.BGR)); - Assert.False(colorType.HasFlag(PixelColorType.Luminance)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); @@ -287,6 +290,5 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Key)); Assert.False(colorType.HasFlag(PixelColorType.CMYK)); Assert.False(colorType.HasFlag(PixelColorType.YCCK)); - Assert.False(colorType.HasFlag(PixelColorType.Indexed)); } } From 8166213c17895ac8159d5b898deeba843441c0f8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 16:55:43 +1000 Subject: [PATCH 151/220] Implement GifFrameMetadata --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 4 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 4 +- .../Formats/Gif/GifDisposalMethod.cs | 37 ---------- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 12 ++-- .../Formats/Gif/GifFrameMetadata.cs | 71 +++++++++++++++---- src/ImageSharp/Formats/Gif/GifMetadata.cs | 4 +- .../Formats/Gif/MetadataExtensions.cs | 12 +--- .../Sections/GifGraphicControlExtension.cs | 6 +- .../Formats/Gif/GifEncoderTests.cs | 8 +-- .../Formats/Gif/GifFrameMetadataTests.cs | 5 +- .../Formats/Gif/GifMetadataTests.cs | 4 +- .../GifGraphicControlExtensionTests.cs | 9 +-- .../Formats/Png/PngEncoderTests.cs | 8 +-- .../Formats/WebP/WebpEncoderTests.cs | 9 +-- .../Metadata/ImageFrameMetadataTests.cs | 3 +- 15 files changed, 101 insertions(+), 95 deletions(-) delete mode 100644 src/ImageSharp/Formats/Gif/GifDisposalMethod.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index 2f03b9c6f..00c5910d4 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -138,8 +138,8 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new(); /// - public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); /// - BmpMetadata IDeepCloneable.DeepClone() => new(this); + public BmpMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index a67ee8f98..e0fe4973d 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -517,7 +517,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } else { - if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToPrevious) + if (this.graphicsControlExtension.DisposalMethod == FrameDisposalMode.RestoreToPrevious) { prevFrame = previousFrame; } @@ -624,7 +624,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals previousFrame = currentFrame ?? image.Frames.RootFrame; - if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToBackground) + if (this.graphicsControlExtension.DisposalMethod == FrameDisposalMode.RestoreToBackground) { this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } diff --git a/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs b/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs deleted file mode 100644 index 12b4239c4..000000000 --- a/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Gif; - -/// -/// Provides enumeration for instructing the decoder what to do with the last image -/// in an animation sequence. -/// section 23 -/// -public enum GifDisposalMethod -{ - /// - /// No disposal specified. - /// The decoder is not required to take any action. - /// - Unspecified = 0, - - /// - /// Do not dispose. - /// The graphic is to be left in place. - /// - NotDispose = 1, - - /// - /// Restore to background color. - /// The area used by the graphic must be restored to the background color. - /// - RestoreToBackground = 2, - - /// - /// Restore to previous. - /// The decoder is required to restore the area overwritten by the - /// graphic with what was there prior to rendering the graphic. - /// - RestoreToPrevious = 3 -} diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 70afe12e1..f4cc166fe 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -235,7 +235,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals Image image, ReadOnlyMemory globalPalette, int globalTransparencyIndex, - GifDisposalMethod previousDisposalMethod) + FrameDisposalMode previousDisposalMode) where TPixel : unmanaged, IPixel { if (image.Frames.Count == 1) @@ -279,10 +279,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals useLocal, gifMetadata, paletteQuantizer, - previousDisposalMethod); + previousDisposalMode); previousFrame = currentFrame; - previousDisposalMethod = gifMetadata.DisposalMethod; + previousDisposalMode = gifMetadata.DisposalMethod; } if (hasPaletteQuantizer) @@ -323,14 +323,14 @@ internal sealed class GifEncoderCore : IImageEncoderInternals bool useLocal, GifFrameMetadata metadata, PaletteQuantizer globalPaletteQuantizer, - GifDisposalMethod previousDisposal) + FrameDisposalMode previousDisposalMode) where TPixel : unmanaged, IPixel { // Capture any explicit transparency index from the metadata. // We use it to determine the value to use to replace duplicate pixels. int transparencyIndex = metadata.HasTransparency ? metadata.TransparencyIndex : -1; - ImageFrame? previous = previousDisposal == GifDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? previous = previousDisposalMode == FrameDisposalMode.RestoreToBackground ? null : previousFrame; // Deduplicate and quantize the frame capturing only required parts. (bool difference, Rectangle bounds) = @@ -664,7 +664,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals bool hasTransparency = metadata.HasTransparency; byte packedValue = GifGraphicControlExtension.GetPackedValue( - disposalMethod: metadata.DisposalMethod, + disposalMode: metadata.DisposalMethod, transparencyFlag: hasTransparency); GifGraphicControlExtension extension = new( diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs index 3f8563706..46bc415ce 100644 --- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Provides Gif specific metadata information for the image frame. /// -public class GifFrameMetadata : IDeepCloneable +public class GifFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -73,10 +73,65 @@ public class GifFrameMetadata : IDeepCloneable /// Primarily used in Gif animation, this field indicates the way in which the graphic is to /// be treated after being displayed. /// - public GifDisposalMethod DisposalMethod { get; set; } + public FrameDisposalMode DisposalMethod { get; set; } + + /// + public static GifFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + { + int index = -1; + const float background = 1f; + if (metadata.ColorTable.HasValue) + { + ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; + for (int i = 0; i < colorTable.Length; i++) + { + Vector4 vector = colorTable[i].ToScaledVector4(); + if (vector.W < background) + { + index = i; + } + } + } + + bool hasTransparency = index >= 0; + + return new() + { + LocalColorTable = metadata.ColorTable, + ColorTableMode = metadata.ColorTableMode, + FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10), + DisposalMethod = metadata.DisposalMode, + HasTransparency = hasTransparency, + TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue, + }; + } + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + { + // throw new NotImplementedException(); + // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or + // has a local palette with 256 colors and is not transparent we should use 'Source'. + bool blendSource = this.DisposalMethod == FrameDisposalMode.RestoreToBackground || (this.LocalColorTable?.Length == 256 && !this.HasTransparency); + + // If the color table is global and frame has no transparency. Consider it 'Source' also. + blendSource |= this.ColorTableMode == FrameColorTableMode.Global && !this.HasTransparency; + + return new() + { + ColorTable = this.LocalColorTable, + ColorTableMode = this.ColorTableMode, + Duration = TimeSpan.FromMilliseconds(this.FrameDelay * 10), + DisposalMode = this.DisposalMethod, + BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); /// - public IDeepCloneable DeepClone() => new GifFrameMetadata(this); + public GifFrameMetadata DeepClone() => new(this); internal static GifFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) { @@ -103,17 +158,9 @@ public class GifFrameMetadata : IDeepCloneable LocalColorTable = metadata.ColorTable, ColorTableMode = metadata.ColorTableMode, FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10), - DisposalMethod = GetMode(metadata.DisposalMode), + DisposalMethod = metadata.DisposalMode, HasTransparency = hasTransparency, TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue, }; } - - private static GifDisposalMethod GetMode(FrameDisposalMode mode) => mode switch - { - FrameDisposalMode.DoNotDispose => GifDisposalMethod.NotDispose, - FrameDisposalMode.RestoreToBackground => GifDisposalMethod.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => GifDisposalMethod.RestoreToPrevious, - _ => GifDisposalMethod.Unspecified, - }; } diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 43935504b..4ebe046ba 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -145,8 +145,8 @@ public class GifMetadata : IFormatMetadata } /// - public IDeepCloneable DeepClone() => ((IDeepCloneable)this).DeepClone(); + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); /// - GifMetadata IDeepCloneable.DeepClone() => new(this); + public GifMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index d4650403c..5fd2d5c1e 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -80,7 +80,7 @@ public static partial class MetadataExtensions { // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or // has a local palette with 256 colors and is not transparent we should use 'Source'. - bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); + bool blendSource = source.DisposalMethod == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); // If the color table is global and frame has no transparency. Consider it 'Source' also. blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency; @@ -90,16 +90,8 @@ public static partial class MetadataExtensions ColorTable = source.LocalColorTable, ColorTableMode = source.ColorTableMode, Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), - DisposalMode = GetMode(source.DisposalMethod), + DisposalMode = source.DisposalMethod, BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, }; } - - private static FrameDisposalMode GetMode(GifDisposalMethod method) => method switch - { - GifDisposalMethod.NotDispose => FrameDisposalMode.DoNotDispose, - GifDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - GifDisposalMethod.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - _ => FrameDisposalMode.Unspecified, - }; } diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs index 52052021f..ad99ac0f4 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs @@ -52,7 +52,7 @@ internal readonly struct GifGraphicControlExtension : IGifExtension, IEquatable< /// Gets the disposal method which indicates the way in which the /// graphic is to be treated after being displayed. /// - public GifDisposalMethod DisposalMethod => (GifDisposalMethod)((this.Packed & 0x1C) >> 2); + public FrameDisposalMode DisposalMethod => (FrameDisposalMode)((this.Packed & 0x1C) >> 2); /// /// Gets a value indicating whether transparency flag is to be set. @@ -80,7 +80,7 @@ internal readonly struct GifGraphicControlExtension : IGifExtension, IEquatable< public static GifGraphicControlExtension Parse(ReadOnlySpan buffer) => MemoryMarshal.Cast(buffer)[0]; - public static byte GetPackedValue(GifDisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false) + public static byte GetPackedValue(FrameDisposalMode disposalMode, bool userInputFlag = false, bool transparencyFlag = false) { /* Reserved | 3 Bits @@ -91,7 +91,7 @@ internal readonly struct GifGraphicControlExtension : IGifExtension, IEquatable< byte value = 0; - value |= (byte)((int)disposalMethod << 2); + value |= (byte)((int)disposalMode << 2); if (userInputFlag) { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 767141f56..0b6615b8e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -309,11 +309,11 @@ public class GifEncoderTests switch (pngF.DisposalMethod) { case PngDisposalMethod.RestoreToBackground: - Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod); break; case PngDisposalMethod.DoNotDispose: default: - Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod); break; } } @@ -359,11 +359,11 @@ public class GifEncoderTests switch (webpF.DisposalMethod) { case WebpDisposalMethod.RestoreToBackground: - Assert.Equal(GifDisposalMethod.RestoreToBackground, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod); break; case WebpDisposalMethod.DoNotDispose: default: - Assert.Equal(GifDisposalMethod.NotDispose, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod); break; } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs index 774638311..f92896b7e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; namespace SixLabors.ImageSharp.Tests.Formats.Gif; @@ -14,14 +15,14 @@ public class GifFrameMetadataTests GifFrameMetadata meta = new() { FrameDelay = 1, - DisposalMethod = GifDisposalMethod.RestoreToBackground, + DisposalMethod = FrameDisposalMode.RestoreToBackground, LocalColorTable = new[] { Color.Black, Color.White } }; GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone(); clone.FrameDelay = 2; - clone.DisposalMethod = GifDisposalMethod.RestoreToPrevious; + clone.DisposalMethod = FrameDisposalMode.RestoreToPrevious; clone.LocalColorTable = new[] { Color.Black }; Assert.False(meta.FrameDelay.Equals(clone.FrameDelay)); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index c7babe932..afaa827bf 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -183,14 +183,14 @@ public class GifMetadataTests } [Theory] - [InlineData(TestImages.Gif.Cheers, 93, FrameColorTableMode.Global, 256, 4, GifDisposalMethod.NotDispose)] + [InlineData(TestImages.Gif.Cheers, 93, FrameColorTableMode.Global, 256, 4, FrameDisposalMode.DoNotDispose)] public void Identify_Frames( string imagePath, int framesCount, FrameColorTableMode colorTableMode, int globalColorTableLength, int frameDelay, - GifDisposalMethod disposalMethod) + FrameDisposalMode disposalMethod) { TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs index c602bc91b..05dc5bc52 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections; @@ -10,9 +11,9 @@ public class GifGraphicControlExtensionTests [Fact] public void TestPackedValue() { - Assert.Equal(0, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.Unspecified, false, false)); - Assert.Equal(11, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToBackground, true, true)); - Assert.Equal(4, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.NotDispose, false, false)); - Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToPrevious, true, false)); + Assert.Equal(0, GifGraphicControlExtension.GetPackedValue(FrameDisposalMode.Unspecified, false, false)); + Assert.Equal(11, GifGraphicControlExtension.GetPackedValue(FrameDisposalMode.RestoreToBackground, true, true)); + Assert.Equal(4, GifGraphicControlExtension.GetPackedValue(FrameDisposalMode.DoNotDispose, false, false)); + Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(FrameDisposalMode.RestoreToPrevious, true, false)); } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 35c446c70..1e4472e8a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -521,14 +521,14 @@ public partial class PngEncoderTests switch (gifF.DisposalMethod) { - case GifDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod); break; - case GifDisposalMethod.RestoreToPrevious: + case FrameDisposalMode.RestoreToPrevious: Assert.Equal(PngDisposalMethod.RestoreToPrevious, pngF.DisposalMethod); break; - case GifDisposalMethod.Unspecified: - case GifDisposalMethod.NotDispose: + case FrameDisposalMode.Unspecified: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod); break; diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index acca49dcf..49c47adfd 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Webp; @@ -96,12 +97,12 @@ public class WebpEncoderTests switch (gifF.DisposalMethod) { - case GifDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); break; - case GifDisposalMethod.RestoreToPrevious: - case GifDisposalMethod.Unspecified: - case GifDisposalMethod.NotDispose: + case FrameDisposalMode.RestoreToPrevious: + case FrameDisposalMode.Unspecified: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); break; diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs index bcc967540..9cb7137fe 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -21,7 +22,7 @@ public class ImageFrameMetadataTests { const int frameDelay = 42; const int colorTableLength = 128; - const GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground; + const FrameDisposalMode disposalMethod = FrameDisposalMode.RestoreToBackground; ImageFrameMetadata metaData = new(); GifFrameMetadata gifFrameMetadata = metaData.GetGifMetadata(); From ff752a22b80638396287bb5c68637b72c5c73f61 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 19:34:53 +1000 Subject: [PATCH 152/220] Sequential layout plus faster equality checks. --- src/ImageSharp/ColorProfiles/CieLab.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/CieLch.cs | 36 +++++++++-------- src/ImageSharp/ColorProfiles/CieLchuv.cs | 24 ++---------- src/ImageSharp/ColorProfiles/CieLuv.cs | 6 ++- .../CieXyChromaticityCoordinates.cs | 7 +++- src/ImageSharp/ColorProfiles/CieXyy.cs | 6 ++- src/ImageSharp/ColorProfiles/CieXyz.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/Cmyk.cs | 6 ++- src/ImageSharp/ColorProfiles/Hsl.cs | 6 ++- src/ImageSharp/ColorProfiles/Hsv.cs | 6 ++- src/ImageSharp/ColorProfiles/HunterLab.cs | 6 ++- src/ImageSharp/ColorProfiles/Lms.cs | 39 ++++++++++++------- src/ImageSharp/ColorProfiles/Rgb.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/YCbCr.cs | 6 ++- 14 files changed, 142 insertions(+), 102 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 82d60f4da..377cc20a9 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents a CIE L*a*b* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLab : IProfileConnectingSpace { /// @@ -80,20 +82,6 @@ public readonly struct CieLab : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLab other) - => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) @@ -171,4 +159,20 @@ public readonly struct CieLab : IProfileConnectingSpace /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLab other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 8f6298a2d..131978340 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLch : IColorProfile { private static readonly Vector3 Min = new(0, -200, 0); @@ -80,22 +82,6 @@ public readonly struct CieLch : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); - /// - public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H); - - /// - public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLch other) - => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); - /// public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) { @@ -159,4 +145,22 @@ public readonly struct CieLch : IColorProfile /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H); + + /// + public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLch other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs index e537259e4..7fd95feb1 100644 --- a/src/ImageSharp/ColorProfiles/CieLchuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLchuv : IColorProfile { private static readonly Vector3 Min = new(0, -200, 0); @@ -159,25 +161,7 @@ public readonly struct CieLchuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLchuv other) - => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 118d32f04..97e2826f7 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -12,6 +13,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// attempted perceptual uniformity /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLuv : IColorProfile { /// @@ -205,7 +207,9 @@ public readonly struct CieLuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLuv other) - => new Vector3(this.L, this.U, this.V) == new Vector3(other.L, other.U, other.V); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs index aad7dbf95..fa12b81d2 100644 --- a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -3,13 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -// ReSharper disable CompareOfFloatsByEqualityOperator namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents the coordinates of CIEXY chromaticity space. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyChromaticityCoordinates : IEquatable { /// @@ -80,5 +81,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyChromaticityCoordinates other) - => new Vector2(this.X, this.Y) == new Vector2(other.X, other.Y); + => this.AsVector2Unsafe() == other.AsVector2Unsafe(); + + private Vector2 AsVector2Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index ea19f5740..62873df14 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an CIE xyY 1931 color /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyy : IColorProfile { /// @@ -150,5 +152,7 @@ public readonly struct CieXyy : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) - => new Vector3(this.X, this.Y, this.Yl) == new Vector3(other.X, other.Y, other.Yl); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index fe8755454..07f9b47f9 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an CIE XYZ 1931 color /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyz : IProfileConnectingSpace { /// @@ -86,20 +88,6 @@ public readonly struct CieXyz : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() => new(this.X, this.Y, this.Z); - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieXyz other) - => new Vector3(this.X, this.Y, this.Z) == new Vector3(other.X, other.Y, other.Z); - /// public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) => new(source.X, source.Y, source.Z); @@ -124,4 +112,20 @@ public readonly struct CieXyz : IProfileConnectingSpace /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieXyz other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs index 88d3249b3..e92490449 100644 --- a/src/ImageSharp/ColorProfiles/Cmyk.cs +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Cmyk : IColorProfile { private static readonly Vector4 Min = Vector4.Zero; @@ -157,5 +159,7 @@ public readonly struct Cmyk : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Cmyk other) - => new Vector4(this.C, this.M, this.Y, this.K) == new Vector4(other.C, other.M, other.Y, other.K); + => this.AsVector4Unsafe() == other.AsVector4Unsafe(); + + private Vector4 AsVector4Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index c7ae97c79..2c98c7df9 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents a Hsl (hue, saturation, lightness) color. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Hsl : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -200,7 +202,9 @@ public readonly struct Hsl : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsl other) - => new Vector3(this.H, this.S, this.L) == new Vector3(other.H, other.S, other.L); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetColorComponent(float first, float second, float third) diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 83445a40f..7535f2463 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Hsv : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -223,5 +225,7 @@ public readonly struct Hsv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) - => new Vector3(this.H, this.S, this.V) == new Vector3(other.H, other.S, other.V); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index eb33e6a95..43ad2ac5c 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an Hunter LAB color. /// . /// +[StructLayout(LayoutKind.Sequential)] public readonly struct HunterLab : IColorProfile { /// @@ -170,7 +172,9 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HunterLab other) - => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index 03a1c5d66..5a6791b2d 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -3,10 +3,17 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; -internal readonly struct Lms : IColorProfile +/// +/// LMS is a color space represented by the response of the three types of cones of the human eye, +/// named after their responsivity (sensitivity) at long, medium and short wavelengths. +/// +/// +[StructLayout(LayoutKind.Sequential)] +public readonly struct Lms : IColorProfile { /// /// Initializes a new instance of the struct. @@ -82,20 +89,6 @@ internal readonly struct Lms : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() => new(this.L, this.M, this.S); - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); - - /// - public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Lms other) - => new Vector3(this.L, this.M, this.S) == new Vector3(other.L, other.M, other.S); - /// public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) { @@ -136,4 +129,20 @@ internal readonly struct Lms : IColorProfile /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); + + /// + public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Lms other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 8036d83e5..664f9d80f 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents an RGB (red, green, blue) color profile. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Rgb : IProfileConnectingSpace { /// @@ -79,20 +81,6 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb other) - => new Vector3(this.R, this.G, this.B) == new Vector3(other.R, other.G, other.B); - /// public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) { @@ -201,6 +189,22 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToScaledVector4() => new(this.ToScaledVector3(), 1f); + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rgb other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); + private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) { Matrix4x4 matrix = GetRgbToCieXyzMatrix(workingSpace); diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index b9f5f65ee..03bd1d312 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -11,6 +12,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct YCbCr : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -152,5 +154,7 @@ public readonly struct YCbCr : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) - => new Vector3(this.Y, this.Cb, this.Cr) == new Vector3(other.Y, other.Cb, other.Cr); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } From cceeb2d81ce6b82d4b2d4a640e3ef098dcaf6484 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 23:10:15 +1000 Subject: [PATCH 153/220] Update Rgb.cs --- src/ImageSharp/ColorProfiles/Rgb.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 664f9d80f..6698e12cb 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -252,15 +252,14 @@ public readonly struct Rgb : IProfileConnectingSpace Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); // Use transposed Rows/Columns - // TODO: Is there a built in method for this multiplication? return new Matrix4x4 { M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb, - M12 = vector.X * 1, - M22 = vector.Y * 1, - M32 = vector.Z * 1, + M12 = vector.X, + M22 = vector.Y, + M32 = vector.Z, M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb, From 5b47e790626446593bca190a462178f91b51cbf5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 23:59:34 +1000 Subject: [PATCH 154/220] Implement JpegMetadata --- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 96 ++++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index ce4c0758f..b12ee524d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -2,18 +2,19 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Provides Jpeg specific metadata information for the image. /// -public class JpegMetadata : IDeepCloneable +public class JpegMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. /// - public JpegMetadata() => this.Comments = new List(); + public JpegMetadata() => this.Comments = []; /// /// Initializes a new instance of the class. @@ -99,5 +100,94 @@ public class JpegMetadata : IDeepCloneable public IList Comments { get; } /// - public IDeepCloneable DeepClone() => new JpegMetadata(this); + public static JpegMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + JpegEncodingColor color; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.YCbCr; + switch (colorType) + { + case PixelColorType.Luminance: + color = JpegEncodingColor.Luminance; + break; + case PixelColorType.CMYK: + color = JpegEncodingColor.Cmyk; + break; + case PixelColorType.YCCK: + color = JpegEncodingColor.Ycck; + break; + default: + if (colorType.HasFlag(PixelColorType.RGB) || colorType.HasFlag(PixelColorType.BGR)) + { + color = JpegEncodingColor.Rgb; + } + else + { + color = metadata.Quality <= Quantization.DefaultQualityFactor + ? JpegEncodingColor.YCbCrRatio420 + : JpegEncodingColor.YCbCrRatio444; + } + + break; + } + + return new JpegMetadata + { + ColorType = color, + ChrominanceQuality = metadata.Quality, + LuminanceQuality = metadata.Quality, + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + int bpp; + PixelColorType colorType; + PixelComponentInfo info; + switch (this.ColorType) + { + case JpegEncodingColor.Luminance: + bpp = 8; + colorType = PixelColorType.Luminance; + info = PixelComponentInfo.Create(1, bpp, 8); + break; + case JpegEncodingColor.Cmyk: + bpp = 32; + colorType = PixelColorType.CMYK; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + break; + case JpegEncodingColor.Ycck: + bpp = 32; + colorType = PixelColorType.YCCK; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + break; + case JpegEncodingColor.Rgb: + bpp = 24; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + default: + bpp = 24; + colorType = PixelColorType.YCbCr; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + } + + return new FormatConnectingMetadata + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, + }, + Quality = this.Quality, + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public JpegMetadata DeepClone() => new(this); } From f62e2baac8957b73f0b910d61549087b40f0761a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:00 +0800 Subject: [PATCH 155/220] Make the BMP Decoder Core can skip the file header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make BmpDecoderCore support AlphaMask. link #687 Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 226 +++++++++++++++--- .../Formats/Bmp/BmpDecoderOptions.cs | 24 ++ 2 files changed, 211 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index bed489752..9bea6867a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -71,7 +71,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// The file header containing general information. /// - private BmpFileHeader fileHeader; + private BmpFileHeader? fileHeader; /// /// Indicates which bitmap file marker was read. @@ -99,6 +99,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private readonly RleSkippedPixelHandling rleSkippedPixelHandling; + /// + private readonly bool processedAlphaMask; + + /// + private readonly bool skipFileHeader; + + /// + private readonly bool isDoubleHeight; + /// /// Initializes a new instance of the class. /// @@ -109,6 +118,9 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; + this.processedAlphaMask = options.ProcessedAlphaMask; + this.skipFileHeader = options.SkipFileHeader; + this.isDoubleHeight = options.IsDoubleHeight; } /// @@ -132,38 +144,44 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals switch (this.infoHeader.Compression) { - case BmpCompression.RGB: - if (this.infoHeader.BitsPerPixel == 32) - { - if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) - { - this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else - { - this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - } - else if (this.infoHeader.BitsPerPixel == 24) - { - this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel == 16) - { - 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, - this.infoHeader.Height, - this.infoHeader.BitsPerPixel, - bytesPerColorMapEntry, - inverted); - } + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32 && this.bmpMetadata.InfoHeaderType is BmpInfoHeaderType.WinVersion3: + this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32: + this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 24: + this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 16: + this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8 && this.processedAlphaMask: + this.ReadRgbPaletteWithAlphaMask( + stream, + pixels, + palette, + this.infoHeader.Width, + this.infoHeader.Height, + this.infoHeader.BitsPerPixel, + bytesPerColorMapEntry, + inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8: + this.ReadRgbPalette( + stream, + pixels, + palette, + this.infoHeader.Width, + this.infoHeader.Height, + this.infoHeader.BitsPerPixel, + bytesPerColorMapEntry, + inverted); break; @@ -839,6 +857,108 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } + /// + private void ReadRgbPaletteWithAlphaMask(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). + int ppb = 8 / bitsPerPixel; + + int arrayWidth = (width + ppb - 1) / ppb; + + // Bit mask + int mask = 0xFF >> (8 - bitsPerPixel); + + // Rows are aligned on 4 byte boundaries. + int padding = arrayWidth % 4; + if (padding != 0) + { + padding = 4 - padding; + } + + Bgra32[,] image = new Bgra32[height, width]; + using (IMemoryOwner row = this.memoryAllocator.Allocate(arrayWidth + padding, AllocationOptions.Clean)) + { + Span rowSpan = row.GetSpan(); + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + if (stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + + int offset = 0; + + for (int x = 0; x < arrayWidth; x++) + { + int colOffset = x * ppb; + for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++) + { + int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; + + image[newY, newX].FromBgr24(Unsafe.As(ref colors[colorIndex])); + } + + offset++; + } + } + } + + arrayWidth = width / 8; + padding = arrayWidth % 4; + if (padding != 0) + { + padding = 4 - padding; + } + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + + for (int i = 0; i < arrayWidth; i++) + { + int x = i * 8; + int and = stream.ReadByte(); + if (and is -1) + { + throw new EndOfStreamException(); + } + + for (int j = 0; j < 8; j++) + { + SetAlpha(ref image[newY, x + j], and, j); + } + } + + stream.Skip(padding); + } + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.DangerousGetRowSpan(newY); + + for (int x = 0; x < width; x++) + { + pixelRow[x].FromBgra32(image[newY, x]); + } + } + } + + /// + /// Set pixel's alpha with alpha mask. + /// + /// Bgra32 pixel. + /// alpha mask. + /// bit index of pixel. + private static void SetAlpha(ref Bgra32 pixel, in int mask, in int index) + { + bool isTransparently = (mask & (0b10000000 >> index)) is not 0; + pixel.A = isTransparently ? byte.MinValue : byte.MaxValue; + } + /// /// Reads the 16 bit color palette from the stream. /// @@ -1333,6 +1453,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.metadata.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution)); } + if (this.isDoubleHeight) + { + this.infoHeader.Height >>= 1; + } + ushort bitsPerPixel = this.infoHeader.BitsPerPixel; this.bmpMetadata = this.metadata.GetBmpMetadata(); this.bmpMetadata.InfoHeaderType = infoHeaderType; @@ -1362,9 +1487,9 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // The bitmap file header of the first image follows the array header. stream.Read(buffer, 0, BmpFileHeader.Size); this.fileHeader = BmpFileHeader.Parse(buffer); - if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) + if (this.fileHeader.Value.Type != BmpConstants.TypeMarkers.Bitmap) { - BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Type}'."); + BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Value.Type}'."); } break; @@ -1387,7 +1512,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals [MemberNotNull(nameof(bmpMetadata))] private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette) { - this.ReadFileHeader(stream); + if (!this.skipFileHeader) + { + this.ReadFileHeader(stream); + } + this.ReadInfoHeader(stream); // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 @@ -1411,7 +1540,21 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals switch (this.fileMarkerType) { case BmpFileMarkerType.Bitmap: - colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + if (this.fileHeader.HasValue) + { + colorMapSizeBytes = this.fileHeader.Value.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + } + else + { + colorMapSizeBytes = this.infoHeader.ClrUsed; + if (colorMapSizeBytes is 0 && this.infoHeader.BitsPerPixel is <= 8) + { + colorMapSizeBytes = ColorNumerics.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); + } + + colorMapSizeBytes *= 4; + } + int colorCountForBitDepth = ColorNumerics.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; @@ -1442,7 +1585,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { // 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 (stream.Position > this.fileHeader.Offset - colorMapSizeBytes) + if (this.fileHeader.HasValue && stream.Position > this.fileHeader.Value.Offset - colorMapSizeBytes) { 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."); @@ -1456,7 +1599,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } - int skipAmount = this.fileHeader.Offset - (int)stream.Position; + int skipAmount = 0; + if (this.fileHeader.HasValue) + { + skipAmount = this.fileHeader.Value.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."); diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index b3387ce80..17d37cab2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -16,4 +16,28 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions /// which can occur during decoding run length encoded bitmaps. /// public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; } + + /// + /// Gets a value indicating whether the additional AlphaMask is processed at decoding time. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool ProcessedAlphaMask { get; init; } + + /// + /// Gets a value indicating whether to skip loading the BMP file header. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool SkipFileHeader { get; init; } + + /// + /// Gets a value indicating whether the height is double of true height. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool IsDoubleHeight { get; init; } } From 520ff46baaf9927a734947d167072a40bd5b85a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:02 +0800 Subject: [PATCH 156/220] Append BMP's MIME Type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Formats/Bmp/BmpConstants.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index 5cf0c9732..62edfdfdf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -11,7 +11,12 @@ internal static class BmpConstants /// /// The list of mimetypes that equate to a bmp. /// - public static readonly IEnumerable MimeTypes = new[] { "image/bmp", "image/x-windows-bmp" }; + public static readonly IEnumerable MimeTypes = new[] + { + "image/bmp", + "image/x-windows-bmp", + "image/x-win-bitmap" + }; /// /// The list of file extensions that equate to a bmp. From 11293824e3213133eb6e1e4bcdec8a67fed3cbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:03 +0800 Subject: [PATCH 157/220] Add UnsafeSetFormatMetadata in ImageFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 1c0330d5d..0567c8916 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -99,6 +99,11 @@ public sealed class ImageFrameMetadata : IDeepCloneable return newMeta; } + internal void UnsafeSetFormatMetadata( + IImageFormat key, + IDeepCloneable value) + => this.formatMetadata[key] = value; + /// /// Gets the metadata value associated with the specified key. /// From 4832d8f24aaadef2827d46f988be0a77bca5018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:03 +0800 Subject: [PATCH 158/220] Add Icon Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ico Decoder - Ico Detector - Ico Detector UnitTest - Cur Decoder - Cur Detector - Cur Detector UnitTest Signed-off-by: 舰队的偶像-岛风酱! --- ImageSharp.sln | 7 + src/ImageSharp/Configuration.cs | 6 +- .../Icon/Cur/CurConfigurationModule.cs | 19 +++ .../Formats/Icon/Cur/CurConstants.cs | 28 ++++ src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs | 47 ++++++ .../Formats/Icon/Cur/CurDecoderCore.cs | 16 +++ src/ImageSharp/Formats/Icon/Cur/CurFormat.cs | 37 +++++ .../Formats/Icon/Cur/CurFrameMetadata.cs | 52 +++++++ .../Formats/Icon/Cur/CurMetadata.cs | 16 +++ .../Formats/Icon/Cur/MetadataExtensions.cs | 44 ++++++ .../Icon/Ico/IcoConfigurationModule.cs | 19 +++ .../Formats/Icon/Ico/IcoConstants.cs | 38 +++++ src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs | 47 ++++++ .../Formats/Icon/Ico/IcoDecoderCore.cs | 16 +++ src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs | 37 +++++ .../Formats/Icon/Ico/IcoFrameMetadata.cs | 50 +++++++ .../Formats/Icon/Ico/IcoMetadata.cs | 16 +++ .../Formats/Icon/Ico/MetadataExtensions.cs | 44 ++++++ src/ImageSharp/Formats/Icon/IconAssert.cs | 43 ++++++ .../Formats/Icon/IconDecoderCore.cs | 136 ++++++++++++++++++ src/ImageSharp/Formats/Icon/IconDir.cs | 26 ++++ src/ImageSharp/Formats/Icon/IconDirEntry.cs | 28 ++++ src/ImageSharp/Formats/Icon/IconFileType.cs | 20 +++ .../Formats/Icon/IconFrameCompression.cs | 25 ++++ .../Formats/Icon/IconFrameMetadata.cs | 103 +++++++++++++ .../Formats/Icon/IconImageFormatDetector.cs | 33 +++++ tests/ImageSharp.Tests/ConfigurationTests.cs | 2 +- .../Formats/Icon/Cur/CurDecoderTests.cs | 23 +++ .../Formats/Icon/Ico/IcoDecoderTests.cs | 23 +++ tests/ImageSharp.Tests/TestImages.cs | 10 ++ tests/Images/Input/Icon/aero_arrow.cur | 3 + tests/Images/Input/Icon/flutter.ico | 3 + 32 files changed, 1015 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurConstants.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurFormat.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/Icon/IconAssert.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDir.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDirEntry.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFileType.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFrameCompression.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs create mode 100644 tests/Images/Input/Icon/aero_arrow.cur create mode 100644 tests/Images/Input/Icon/flutter.ico diff --git a/ImageSharp.sln b/ImageSharp.sln index 162de8416..7ccd92c07 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -661,6 +661,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493 tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Icon", "Icon", "{95E45DDE-A67D-48AD-BBA8-5FAA151B860D}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Icon\aero_arrow.cur = tests\Images\Input\Icon\aero_arrow.cur + tests\Images\Input\Icon\flutter.ico = tests\Images\Input\Icon\flutter.ico + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -714,6 +720,7 @@ Global {670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} {5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} {E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} + {95E45DDE-A67D-48AD-BBA8-5FAA151B860D} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 1ca5d0a46..d6cfd480d 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -5,6 +5,8 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; @@ -222,5 +224,7 @@ public sealed class Configuration new TgaConfigurationModule(), new TiffConfigurationModule(), new WebpConfigurationModule(), - new QoiConfigurationModule()); + new QoiConfigurationModule(), + new IcoConfigurationModule(), + new CurConfigurationModule()); } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs new file mode 100644 index 000000000..c975bc609 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Registers the image encoders, decoders and mime type detectors for the Ico format. +/// +public sealed class CurConfigurationModule : IImageFormatConfigurationModule +{ + /// + public void Configure(Configuration configuration) + { + // TODO: CurEncoder + // configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); + configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance); + configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); + } +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs b/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs new file mode 100644 index 000000000..701b40cf4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Defines constants relating to ICOs +/// +internal static class CurConstants +{ + /// + /// The list of mimetypes that equate to a ico. + /// + /// + /// See + /// + public static readonly IEnumerable MimeTypes = new[] + { + "application/octet-stream", + }; + + /// + /// The list of file extensions that equate to a ico. + /// + public static readonly IEnumerable FileExtensions = new[] { "cur" }; + + public const uint FileHeader = 0x00_02_00_00; +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs new file mode 100644 index 000000000..ceefdcaba --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Decoder for generating an image out of a ico encoded stream. +/// +public sealed class CurDecoder : ImageDecoder +{ + private CurDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static CurDecoder Instance { get; } = new(); + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + Image image = new CurDecoderCore(options).Decode(options.Configuration, stream, cancellationToken); + + ScaleToTargetSize(options, image); + + return image; + } + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new CurDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs new file mode 100644 index 000000000..8b08f127d --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +internal sealed class CurDecoderCore : IconDecoderCore +{ + public CurDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs b/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs new file mode 100644 index 000000000..1e5758bc4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ICO format. +/// +public sealed class CurFormat : IImageFormat +{ + private CurFormat() + { + } + + /// + /// Gets the shared instance. + /// + public static CurFormat Instance { get; } = new(); + + /// + public string Name => "ICO"; + + /// + public string DefaultMimeType => CurConstants.MimeTypes.First(); + + /// + public IEnumerable MimeTypes => CurConstants.MimeTypes; + + /// + public IEnumerable FileExtensions => CurConstants.FileExtensions; + + /// + public CurMetadata CreateDefaultFormatMetadata() => new(); + + /// + public CurFrameMetadata CreateDefaultFormatFrameMetadata() => new(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs new file mode 100644 index 000000000..c94afdd3a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// IcoFrameMetadata +/// +public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + public CurFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// metadata + public CurFrameMetadata(IconFrameMetadata metadata) + : base(metadata) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + : base(width, height, colorCount, field1, field2) + { + } + + /// + /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + /// + public ushort HotspotX { get => this.Field1; set => this.Field1 = value; } + + /// + /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. + /// + public ushort HotspotY { get => this.Field2; set => this.Field2 = value; } + + /// + public override CurFrameMetadata DeepClone() => new(this); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs new file mode 100644 index 000000000..ed3c322b4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Provides Ico specific metadata information for the image. +/// +public class CurMetadata : IDeepCloneable, IDeepCloneable +{ + /// + public CurMetadata DeepClone() => new(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs new file mode 100644 index 000000000..400ece648 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Extension methods for the type. +/// +public static class MetadataExtensions +{ + /// + /// Gets the Icon format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static CurMetadata GetCurMetadata(this ImageMetadata source) + => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Gets the Icon format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source) + => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Gets the Icon format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// + /// When this method returns, contains the metadata associated with the specified frame, + /// if found; otherwise, the default value for the type of the metadata parameter. + /// This parameter is passed uninitialized. + /// + /// + /// if the Icon frame metadata exists; otherwise, . + /// + public static bool TryGetCurMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out CurFrameMetadata? metadata) + => source.TryGetFormatMetadata(CurFormat.Instance, out metadata); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs new file mode 100644 index 000000000..7074189c7 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Registers the image encoders, decoders and mime type detectors for the Ico format. +/// +public sealed class IcoConfigurationModule : IImageFormatConfigurationModule +{ + /// + public void Configure(Configuration configuration) + { + // TODO: IcoEncoder + // configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); + configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance); + configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); + } +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs new file mode 100644 index 000000000..345711705 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Defines constants relating to ICOs +/// +internal static class IcoConstants +{ + /// + /// The list of mimetypes that equate to a ico. + /// + /// + /// See + /// + public static readonly IEnumerable MimeTypes = new[] + { + // IANA-registered + "image/vnd.microsoft.icon", + + // ICO & CUR types used by Windows + "image/x-icon", + + // Erroneous types but have been used + "image/ico", + "image/icon", + "text/ico", + "application/ico", + }; + + /// + /// The list of file extensions that equate to a ico. + /// + public static readonly IEnumerable FileExtensions = new[] { "ico" }; + + public const uint FileHeader = 0x00_01_00_00; +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs new file mode 100644 index 000000000..5d6137920 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Decoder for generating an image out of a ico encoded stream. +/// +public sealed class IcoDecoder : ImageDecoder +{ + private IcoDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static IcoDecoder Instance { get; } = new(); + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + Image image = new IcoDecoderCore(options).Decode(options.Configuration, stream, cancellationToken); + + ScaleToTargetSize(options, image); + + return image; + } + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new IcoDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs new file mode 100644 index 000000000..0782c2128 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +internal sealed class IcoDecoderCore : IconDecoderCore +{ + public IcoDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetIcoMetadata(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs new file mode 100644 index 000000000..27b0525bf --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ICO format. +/// +public sealed class IcoFormat : IImageFormat +{ + private IcoFormat() + { + } + + /// + /// Gets the shared instance. + /// + public static IcoFormat Instance { get; } = new(); + + /// + public string Name => "ICO"; + + /// + public string DefaultMimeType => IcoConstants.MimeTypes.First(); + + /// + public IEnumerable MimeTypes => IcoConstants.MimeTypes; + + /// + public IEnumerable FileExtensions => IcoConstants.FileExtensions; + + /// + public IcoMetadata CreateDefaultFormatMetadata() => new(); + + /// + public IcoFrameMetadata CreateDefaultFormatFrameMetadata() => new(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs new file mode 100644 index 000000000..7c903facd --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// IcoFrameMetadata +/// +public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + public IcoFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// metadata + public IcoFrameMetadata(IconFrameMetadata metadata) + : base(metadata) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + public IcoFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + : base(width, height, colorCount, field1, field2) + { + } + + /// + /// Gets or sets Specifies bits per pixel. + /// + /// + /// It may used by Encoder. + /// + public ushort BitCount { get => this.Field2; set => this.Field2 = value; } + + /// + public override IcoFrameMetadata DeepClone() => new(this); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs new file mode 100644 index 000000000..b227d0bd6 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Provides Ico specific metadata information for the image. +/// +public class IcoMetadata : IDeepCloneable, IDeepCloneable +{ + /// + public IcoMetadata DeepClone() => new(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs new file mode 100644 index 000000000..fb5b4afe7 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Extension methods for the type. +/// +public static class MetadataExtensions +{ + /// + /// Gets the Ico format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static IcoMetadata GetIcoMetadata(this ImageMetadata source) + => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Gets the Ico format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static IcoFrameMetadata GetIcoMetadata(this ImageFrameMetadata source) + => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Gets the Ico format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// + /// When this method returns, contains the metadata associated with the specified frame, + /// if found; otherwise, the default value for the type of the metadata parameter. + /// This parameter is passed uninitialized. + /// + /// + /// if the Ico frame metadata exists; otherwise, . + /// + public static bool TryGetIcoMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out IcoFrameMetadata? metadata) + => source.TryGetFormatMetadata(IcoFormat.Instance, out metadata); +} diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs new file mode 100644 index 000000000..bcb427c1a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal class IconAssert +{ + internal static void CanSeek(Stream stream) + { + if (!stream.CanSeek) + { + throw new NotSupportedException("This stream cannot support seek"); + } + } + + internal static int EndOfStream(int v, int length) + { + if (v != length) + { + throw new EndOfStreamException(); + } + + return v; + } + + internal static long EndOfStream(long v, long length) + { + if (v != length) + { + throw new EndOfStreamException(); + } + + return v; + } + + internal static void NotSquare(in Size size) + { + if (size.Width != size.Height) + { + throw new FormatException("This image is not square."); + } + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs new file mode 100644 index 000000000..d9a578ff2 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal abstract class IconDecoderCore : IImageDecoderInternals +{ + private IconDir fileHeader; + + public IconDecoderCore(DecoderOptions options) => this.Options = options; + + public DecoderOptions Options { get; } + + public Size Dimensions { get; private set; } + + protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } + + protected IconDirEntry[] Entries { get; private set; } = Array.Empty(); + + public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + // Stream may not at 0. + long basePosition = stream.Position; + this.ReadHeader(stream); + + Span flag = stackalloc byte[Png.PngConstants.HeaderBytes.Length]; + + List<(Image Image, bool IsPng, int Index)> frames = new(this.Entries.Length); + for (int i = 0; i < this.Entries.Length; i++) + { + _ = IconAssert.EndOfStream(stream.Seek(basePosition + this.Entries[i].ImageOffset, SeekOrigin.Begin), basePosition + this.Entries[i].ImageOffset); + _ = IconAssert.EndOfStream(stream.Read(flag), Png.PngConstants.HeaderBytes.Length); + _ = stream.Seek(-Png.PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(Png.PngConstants.HeaderBytes); + + Image img = this.GetDecoder(isPng).Decode(stream, cancellationToken); + IconAssert.NotSquare(img.Size); + frames.Add((img, isPng, i)); + if (isPng && img.Size.Width > this.Dimensions.Width) + { + this.Dimensions = img.Size; + } + } + + ImageMetadata metadata = new(); + return new(this.Options.Configuration, metadata, frames.Select(i => + { + ImageFrame target = new(this.Options.Configuration, this.Dimensions); + ImageFrame source = i.Image.Frames.RootFrameUnsafe; + for (int h = 0; h < source.Height; h++) + { + source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); + } + + if (i.IsPng) + { + target.Metadata.UnsafeSetFormatMetadata(Png.PngFormat.Instance, i.Image.Metadata.GetPngMetadata()); + } + else + { + target.Metadata.UnsafeSetFormatMetadata(Bmp.BmpFormat.Instance, i.Image.Metadata.GetBmpMetadata()); + } + + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[i.Index]); + + i.Image.Dispose(); + return target; + }).ToArray()); + } + + public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + this.ReadHeader(stream); + + ImageMetadata metadata = new(); + ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; + for (int i = 0; i < frames.Length; i++) + { + frames[i] = new(); + IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); + icoFrameMetadata.FromIconDirEntry(this.Entries[i]); + } + + return new(new(32), new(0), metadata, frames); + } + + protected abstract IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata); + + protected void ReadHeader(Stream stream) + { + _ = Read(stream, out this.fileHeader, IconDir.Size); + this.Entries = new IconDirEntry[this.FileHeader.Count]; + for (int i = 0; i < this.Entries.Length; i++) + { + _ = Read(stream, out this.Entries[i], IconDirEntry.Size); + } + + this.Dimensions = new( + this.Entries.Max(i => i.Width), + this.Entries.Max(i => i.Height)); + } + + private static int Read(Stream stream, out T data, int size) + where T : unmanaged + { + Span buffer = stackalloc byte[size]; + _ = IconAssert.EndOfStream(stream.Read(buffer), size); + data = MemoryMarshal.Cast(buffer)[0]; + return size; + } + + private IImageDecoderInternals GetDecoder(bool isPng) + { + if (isPng) + { + return new Png.PngDecoderCore(this.Options); + } + else + { + return new Bmp.BmpDecoderCore(new() + { + ProcessedAlphaMask = true, + SkipFileHeader = true, + IsDoubleHeight = true, + }); + } + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs new file mode 100644 index 000000000..f1281a568 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] +internal struct IconDir +{ + public const int Size = 3 * sizeof(ushort); + public ushort Reserved; + public IconFileType Type; + public ushort Count; + + public IconDir(IconFileType type) + : this(type, 0) + => this.Type = type; + + public IconDir(IconFileType type, ushort count) + { + this.Reserved = 0; + this.Type = type; + this.Count = count; + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs new file mode 100644 index 000000000..43254f89d --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] +internal struct IconDirEntry +{ + public const int Size = (4 * sizeof(byte)) + (2 * sizeof(ushort)) + (2 * sizeof(uint)); + + public byte Width; + + public byte Height; + + public byte ColorCount; + + public byte Reserved; + + public ushort Planes; + + public ushort BitCount; + + public uint BytesInRes; + + public uint ImageOffset; +} diff --git a/src/ImageSharp/Formats/Icon/IconFileType.cs b/src/ImageSharp/Formats/Icon/IconFileType.cs new file mode 100644 index 000000000..3450698f1 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFileType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// Ico file type +/// +internal enum IconFileType : ushort +{ + /// + /// ICO file + /// + ICO = 1, + + /// + /// CUR file + /// + CUR = 2, +} diff --git a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs new file mode 100644 index 000000000..f6509f40c --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// IconFrameCompression +/// +public enum IconFrameCompression +{ + /// + /// Unknown + /// + Unknown, + + /// + /// Bmp + /// + Bmp, + + /// + /// Png + /// + Png +} diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs new file mode 100644 index 000000000..17e641c71 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// IconFrameMetadata +/// +public abstract class IconFrameMetadata : IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + protected IconFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + protected IconFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; + this.Field1 = field1; + this.Field2 = field2; + } + + /// + protected IconFrameMetadata(IconFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.Field1 = metadata.Field1; + this.Field2 = metadata.Field2; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; protected set; } + + /// + /// Gets or sets ColorCount field.
    + /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
    + // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + + /// + /// Gets or sets Planes field.
    + /// In ICO format: Specifies color planes. Should be 0 or 1.
    + /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + ///
    + protected ushort Field1 { get; set; } + + /// + /// Gets or sets BitCount field.
    + /// In ICO format: Specifies bits per pixel.
    + /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. + ///
    + protected ushort Field2 { get; set; } + + /// + /// Gets or sets Height field.
    + /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + ///
    + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
    + /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
    + public byte EncodingWidth { get; set; } + + /// + public abstract IDeepCloneable DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry metadata) + { + this.EncodingWidth = metadata.Width; + this.EncodingHeight = metadata.Height; + this.ColorCount = metadata.ColorCount; + this.Field1 = metadata.Planes; + this.Field2 = metadata.BitCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = this.Field1, + BitCount = this.Field2, + }; +} diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs new file mode 100644 index 000000000..aff8dfe0a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// Detects ico file headers. +/// +public class IconImageFormatDetector : IImageFormatDetector +{ + /// + public int HeaderSize { get; } = 4; + + /// + public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) + { + switch (MemoryMarshal.Cast(header)[0]) + { + case Ico.IcoConstants.FileHeader: + format = Ico.IcoFormat.Instance; + return true; + case Cur.CurConstants.FileHeader: + format = Cur.CurFormat.Instance; + return true; + default: + format = default; + return false; + } + } +} diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index c5d61726c..c8e6cd265 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -20,7 +20,7 @@ public class ConfigurationTests public Configuration DefaultConfiguration { get; } - private readonly int expectedDefaultConfigurationCount = 9; + private readonly int expectedDefaultConfigurationCount = 11; public ConfigurationTests() { diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs new file mode 100644 index 000000000..c3c9ad1c4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Cur; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; + +[Trait("Format", "Cur")] +[ValidateDisposedMemoryAllocations] +public class CurDecoderTests +{ + [Theory] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CurDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + + image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs new file mode 100644 index 000000000..6e7b35111 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Ico; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; + +[Trait("Format", "Icon")] +[ValidateDisposedMemoryAllocations] +public class IcoDecoderTests +{ + [Theory] + [WithFile(Flutter, PixelTypes.Rgba32)] + public void IcoDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a45931e29..c64bf2b34 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1121,4 +1121,14 @@ public static class TestImages public const string TestCardRGBA = "Qoi/testcard_rgba.qoi"; public const string Wikipedia008 = "Qoi/wikipedia_008.qoi"; } + + public static class Ico + { + public const string Flutter = "Icon/flutter.ico"; + } + + public static class Cur + { + public const string WindowsMouse = "Icon/aero_arrow.cur"; + } } diff --git a/tests/Images/Input/Icon/aero_arrow.cur b/tests/Images/Input/Icon/aero_arrow.cur new file mode 100644 index 000000000..82cbbd33e --- /dev/null +++ b/tests/Images/Input/Icon/aero_arrow.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06678bbf954f0bece61062633dc63a52a34a6f3c27ac7108f28c0f0d26bb22a7 +size 136606 diff --git a/tests/Images/Input/Icon/flutter.ico b/tests/Images/Input/Icon/flutter.ico new file mode 100644 index 000000000..4001f1426 --- /dev/null +++ b/tests/Images/Input/Icon/flutter.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c098d3fc85cacff98b8e69811b48e9f0d852fcee278132d794411d978869cbf8 +size 33772 From ffde9a9380698c19b693847ad24f7789ccae9207 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:53 +0800 Subject: [PATCH 159/220] Refactor decoder and add notes --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 4 +- .../Formats/Bmp/BmpDecoderOptions.cs | 12 +- .../Formats/Icon/Cur/CurDecoderCore.cs | 2 + .../Formats/Icon/Cur/CurFrameMetadata.cs | 2 +- .../Formats/Icon/Ico/IcoDecoderCore.cs | 2 + .../Formats/Icon/Ico/IcoFrameMetadata.cs | 8 +- src/ImageSharp/Formats/Icon/IconAssert.cs | 8 -- .../Formats/Icon/IconDecoderCore.cs | 107 ++++++++++++------ .../Formats/Icon/IconFrameCompression.cs | 5 - .../Formats/Icon/IconFrameMetadata.cs | 1 + src/ImageSharp/Metadata/ImageFrameMetadata.cs | 6 +- src/ImageSharp/Metadata/ImageMetadata.cs | 3 + .../Formats/Icon/Cur/CurDecoderTests.cs | 7 +- .../Formats/Icon/Ico/IcoDecoderTests.cs | 5 +- 14 files changed, 106 insertions(+), 66 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 9bea6867a..568eea00a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -105,7 +105,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private readonly bool skipFileHeader; - /// + /// private readonly bool isDoubleHeight; /// @@ -120,7 +120,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.memoryAllocator = this.configuration.MemoryAllocator; this.processedAlphaMask = options.ProcessedAlphaMask; this.skipFileHeader = options.SkipFileHeader; - this.isDoubleHeight = options.IsDoubleHeight; + this.isDoubleHeight = options.UseDoubleHeight; } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index 17d37cab2..158a9d479 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -18,10 +18,10 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; } /// - /// Gets a value indicating whether the additional AlphaMask is processed at decoding time. + /// Gets a value indicating whether the additional alpha mask is processed at decoding time. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// internal bool ProcessedAlphaMask { get; init; } @@ -29,15 +29,15 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions /// Gets a value indicating whether to skip loading the BMP file header. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// internal bool SkipFileHeader { get; init; } /// - /// Gets a value indicating whether the height is double of true height. + /// Gets a value indicating whether to treat the height as double of true height. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// - internal bool IsDoubleHeight { get; init; } + internal bool UseDoubleHeight { get; init; } } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index 8b08f127d..44fcc8fcc 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -3,6 +3,8 @@ using SixLabors.ImageSharp.Metadata; +// TODO: flatten namespace. +// namespace SixLabors.ImageSharp.Formats.Cur; namespace SixLabors.ImageSharp.Formats.Icon.Cur; internal sealed class CurDecoderCore : IconDecoderCore diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs index c94afdd3a..60ced16ea 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Cur; /// -/// IcoFrameMetadata +/// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable { diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 0782c2128..677a9c07a 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -3,6 +3,8 @@ using SixLabors.ImageSharp.Metadata; +// TODO: flatten namespace. +// namespace SixLabors.ImageSharp.Formats.Ico; namespace SixLabors.ImageSharp.Formats.Icon.Ico; internal sealed class IcoDecoderCore : IconDecoderCore diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs index 7c903facd..4f557715d 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Ico; /// -/// IcoFrameMetadata +/// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable { @@ -38,11 +38,9 @@ public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Gets or sets Specifies bits per pixel. + /// Gets or sets the bits per pixel. + /// TODO: This needs to be constrained and calculated using the metadata returned from the png/bmp. ///
    - /// - /// It may used by Encoder. - /// public ushort BitCount { get => this.Field2; set => this.Field2 = value; } /// diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index bcb427c1a..04a9527b9 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -32,12 +32,4 @@ internal class IconAssert return v; } - - internal static void NotSquare(in Size size) - { - if (size.Width != size.Height) - { - throw new FormatException("This image is not square."); - } - } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index d9a578ff2..d7fc25c69 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -2,7 +2,8 @@ // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; -using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -30,50 +31,63 @@ internal abstract class IconDecoderCore : IImageDecoderInternals long basePosition = stream.Position; this.ReadHeader(stream); - Span flag = stackalloc byte[Png.PngConstants.HeaderBytes.Length]; + Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; + Image result = new(this.Dimensions.Width, this.Dimensions.Height); - List<(Image Image, bool IsPng, int Index)> frames = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { - _ = IconAssert.EndOfStream(stream.Seek(basePosition + this.Entries[i].ImageOffset, SeekOrigin.Begin), basePosition + this.Entries[i].ImageOffset); - _ = IconAssert.EndOfStream(stream.Read(flag), Png.PngConstants.HeaderBytes.Length); - _ = stream.Seek(-Png.PngConstants.HeaderBytes.Length, SeekOrigin.Current); + ref IconDirEntry entry = ref this.Entries[i]; - bool isPng = flag.SequenceEqual(Png.PngConstants.HeaderBytes); + // If we hit the end of the stream we should break. + if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) + { + break; + } - Image img = this.GetDecoder(isPng).Decode(stream, cancellationToken); - IconAssert.NotSquare(img.Size); - frames.Add((img, isPng, i)); - if (isPng && img.Size.Width > this.Dimensions.Width) + // There should always be enough bytes for this regardless of the entry type. + if (stream.Read(flag) != PngConstants.HeaderBytes.Length) { - this.Dimensions = img.Size; + break; } - } - ImageMetadata metadata = new(); - return new(this.Options.Configuration, metadata, frames.Select(i => - { - ImageFrame target = new(this.Options.Configuration, this.Dimensions); - ImageFrame source = i.Image.Frames.RootFrameUnsafe; + // Reset the stream position. + stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); + using Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); + + ImageFrame source = temp.Frames.RootFrameUnsafe; + ImageFrame target = i == 0 ? result.Frames.RootFrameUnsafe : result.Frames.CreateFrame(); + + // Draw the new frame at position 0,0. We capture the dimensions for cropping during encoding + // via the icon entry. for (int h = 0; h < source.Height; h++) { source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); } - if (i.IsPng) + // Copy the format specific metadata to the image. + if (isPng) { - target.Metadata.UnsafeSetFormatMetadata(Png.PngFormat.Instance, i.Image.Metadata.GetPngMetadata()); + if (i == 0) + { + result.Metadata.SetFormatMetadata(PngFormat.Instance, temp.Metadata.GetPngMetadata()); + } + + target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else + else if (i == 0) { - target.Metadata.UnsafeSetFormatMetadata(Bmp.BmpFormat.Instance, i.Image.Metadata.GetBmpMetadata()); + // Bmp does not contain frame specific metadata. + result.Metadata.SetFormatMetadata(BmpFormat.Instance, temp.Metadata.GetBmpMetadata()); } - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[i.Index]); + // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) + // so we can avoid the protected Field1 and Field2 properties and use strong typing. + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(entry); + } - i.Image.Dispose(); - return target; - }).ToArray()); + return result; } public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) @@ -84,11 +98,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; for (int i = 0; i < frames.Length; i++) { + // TODO: Use the Identify methods in each decoder to return the + // format specific metadata for the image and frame. frames[i] = new(); IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); icoFrameMetadata.FromIconDirEntry(this.Entries[i]); } + // TODO: Use real values from the metadata. return new(new(32), new(0), metadata, frames); } @@ -96,6 +113,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals protected void ReadHeader(Stream stream) { + // TODO: Check length and throw if the header cannot be read. _ = Read(stream, out this.fileHeader, IconDir.Size); this.Entries = new IconDirEntry[this.FileHeader.Count]; for (int i = 0; i < this.Entries.Length; i++) @@ -103,15 +121,39 @@ internal abstract class IconDecoderCore : IImageDecoderInternals _ = Read(stream, out this.Entries[i], IconDirEntry.Size); } - this.Dimensions = new( - this.Entries.Max(i => i.Width), - this.Entries.Max(i => i.Height)); + int width = 0; + int height = 0; + foreach (IconDirEntry entry in this.Entries) + { + if (entry.Width == 0) + { + width = 256; + } + + if (entry.Height == 0) + { + height = 256; + } + + if (width == 256 && height == 256) + { + break; + } + + width = Math.Max(width, entry.Width); + height = Math.Max(height, entry.Height); + } + + this.Dimensions = new(width, height); } private static int Read(Stream stream, out T data, int size) where T : unmanaged { + // TODO: Use explicit parsing methods for each T type. + // See PngHeader.Parse() for example. Span buffer = stackalloc byte[size]; + _ = IconAssert.EndOfStream(stream.Read(buffer), size); data = MemoryMarshal.Cast(buffer)[0]; return size; @@ -121,15 +163,16 @@ internal abstract class IconDecoderCore : IImageDecoderInternals { if (isPng) { - return new Png.PngDecoderCore(this.Options); + return new PngDecoderCore(this.Options); } else { - return new Bmp.BmpDecoderCore(new() + return new BmpDecoderCore(new() { + GeneralOptions = this.Options, ProcessedAlphaMask = true, SkipFileHeader = true, - IsDoubleHeight = true, + UseDoubleHeight = true, }); } } diff --git a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs index f6509f40c..5c772c3fe 100644 --- a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs +++ b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs @@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Icon; ///
    public enum IconFrameCompression { - /// - /// Unknown - /// - Unknown, - /// /// Bmp /// diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs index 17e641c71..5c23388e7 100644 --- a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.Formats.Icon; /// /// IconFrameMetadata +/// TODO: Delete this. Treat the individual metadata types as separate types so we can avoid Field1, Field2 and use strong typing with constaints. /// public abstract class IconFrameMetadata : IDeepCloneable { diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 0567c8916..562e47803 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -99,9 +99,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable return newMeta; } - internal void UnsafeSetFormatMetadata( - IImageFormat key, - IDeepCloneable value) + internal void SetFormatMetadata(IImageFormat key, TFormatFrameMetadata value) + where TFormatMetadata : class + where TFormatFrameMetadata : class, IDeepCloneable => this.formatMetadata[key] = value; /// diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 6b62be08f..d9aba6631 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -215,6 +215,9 @@ public sealed class ImageMetadata : IDeepCloneable metadata = default; return false; } + internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) + where TFormatMetadata : class, IDeepCloneable + => this.formatMetadata[key] = value; /// public ImageMetadata DeepClone() => new(this); diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index c3c9ad1c4..f78c04cdf 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Icon.Cur; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -18,6 +17,10 @@ public class CurDecoderTests { using Image image = provider.GetImage(CurDecoder.Instance); - image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + image.DebugSaveMultiFrame(provider, extension: "png"); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 6e7b35111..86f8b003b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Icon.Ico; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -18,6 +17,8 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } From 79f538723f6500350be073bf66bfc22d65784d56 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:54 +0800 Subject: [PATCH 160/220] Handle frames exceeding 256 pixels --- .../Formats/Icon/IconDecoderCore.cs | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index d7fc25c69..874e93411 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -32,7 +32,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals this.ReadHeader(stream); Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - Image result = new(this.Dimensions.Width, this.Dimensions.Height); + + List<(Image Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { @@ -54,37 +55,62 @@ internal abstract class IconDecoderCore : IImageDecoderInternals stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); - using Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); - ImageFrame source = temp.Frames.RootFrameUnsafe; - ImageFrame target = i == 0 ? result.Frames.RootFrameUnsafe : result.Frames.CreateFrame(); + // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. + Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); + decodedEntries.Add((temp, isPng, i)); + + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. + this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); + } - // Draw the new frame at position 0,0. We capture the dimensions for cropping during encoding - // via the icon entry. - for (int h = 0; h < source.Height; h++) + ImageMetadata metadata = new(); + BmpMetadata? bmpMetadata = null; + PngMetadata? pngMetadata = null; + Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => + { + ImageFrame target = new(this.Options.Configuration, this.Dimensions); + ImageFrame source = x.Image.Frames.RootFrameUnsafe; + for (int y = 0; y < source.Height; y++) { - source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); + source.PixelBuffer.DangerousGetRowSpan(y).CopyTo(target.PixelBuffer.DangerousGetRowSpan(y)); } - // Copy the format specific metadata to the image. - if (isPng) + // Copy the format specific frame metadata to the image. + if (x.IsPng) { - if (i == 0) + if (x.Index == 0) { - result.Metadata.SetFormatMetadata(PngFormat.Instance, temp.Metadata.GetPngMetadata()); + pngMetadata = x.Image.Metadata.GetPngMetadata(); } + // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else if (i == 0) + else if (x.Index == 0) { - // Bmp does not contain frame specific metadata. - result.Metadata.SetFormatMetadata(BmpFormat.Instance, temp.Metadata.GetBmpMetadata()); + bmpMetadata = x.Image.Metadata.GetBmpMetadata(); } // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) // so we can avoid the protected Field1 and Field2 properties and use strong typing. - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(entry); + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[x.Index]); + + x.Image.Dispose(); + + return target; + }).ToArray()); + + // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + + if (pngMetadata != null) + { + result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); } return result; @@ -125,6 +151,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals int height = 0; foreach (IconDirEntry entry in this.Entries) { + // Since Windows 95 size of an image in the ICONDIRENTRY structure might + // be set to zero, which means 256 pixels. if (entry.Width == 0) { width = 256; From a811784d34c87e46322f8b8e1d8b3f48ebe9e3ef Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:55 +0800 Subject: [PATCH 161/220] Update TgaFileHeaderTests.cs --- tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index bf24ba350..2f96b6d43 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -9,6 +9,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga; [Trait("Format", "Tga")] public class TgaFileHeaderTests { + // TODO: Some of these clash with the ICO magic bytes. Check correctness. + // https://en.wikipedia.org/wiki/Truevision_TGA#Header [Theory] [InlineData(new byte[] { 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 })] // invalid tga image type. [InlineData(new byte[] { 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 })] // invalid colormap type. From 43ea25fe8b612cf1da5713b1149ac2a078be64e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:55 +0800 Subject: [PATCH 162/220] Add Parse Method and Check Stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconDecoderCore.cs | 24 +++++++------------ src/ImageSharp/Formats/Icon/IconDir.cs | 15 +++++++++--- src/ImageSharp/Formats/Icon/IconDirEntry.cs | 3 +++ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 874e93411..1077bde51 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -139,12 +139,18 @@ internal abstract class IconDecoderCore : IImageDecoderInternals protected void ReadHeader(Stream stream) { - // TODO: Check length and throw if the header cannot be read. - _ = Read(stream, out this.fileHeader, IconDir.Size); + Span buffer = stackalloc byte[IconDirEntry.Size]; + + // ICONDIR + _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); + this.fileHeader = IconDir.Parse(buffer); + + // ICONDIRENTRY this.Entries = new IconDirEntry[this.FileHeader.Count]; for (int i = 0; i < this.Entries.Length; i++) { - _ = Read(stream, out this.Entries[i], IconDirEntry.Size); + _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); + this.Entries[i] = IconDirEntry.Parse(buffer); } int width = 0; @@ -175,18 +181,6 @@ internal abstract class IconDecoderCore : IImageDecoderInternals this.Dimensions = new(width, height); } - private static int Read(Stream stream, out T data, int size) - where T : unmanaged - { - // TODO: Use explicit parsing methods for each T type. - // See PngHeader.Parse() for example. - Span buffer = stackalloc byte[size]; - - _ = IconAssert.EndOfStream(stream.Read(buffer), size); - data = MemoryMarshal.Cast(buffer)[0]; - return size; - } - private IImageDecoderInternals GetDecoder(bool isPng) { if (isPng) diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index f1281a568..53b87bc9f 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; @@ -15,12 +15,21 @@ internal struct IconDir public IconDir(IconFileType type) : this(type, 0) - => this.Type = type; + { + } public IconDir(IconFileType type, ushort count) + : this(0, type, count) + { + } + + public IconDir(ushort reserved, IconFileType type, ushort count) { - this.Reserved = 0; + this.Reserved = reserved; this.Type = type; this.Count = count; } + + public static IconDir Parse(in ReadOnlySpan data) + => MemoryMarshal.Cast(data)[0]; } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index 43254f89d..edd778f7e 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -25,4 +25,7 @@ internal struct IconDirEntry public uint BytesInRes; public uint ImageOffset; + + public static IconDirEntry Parse(in ReadOnlySpan data) + => MemoryMarshal.Cast(data)[0]; } From 05d33f2895db1505940b92427b164091088cece6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:56 +0800 Subject: [PATCH 163/220] Remove IconFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderCore.cs | 2 +- .../Formats/Icon/Cur/CurFrameMetadata.cs | 90 ++++++++++++--- .../Formats/Icon/Ico/IcoDecoderCore.cs | 3 +- .../Formats/Icon/Ico/IcoFrameMetadata.cs | 80 +++++++++++--- .../Formats/Icon/IconDecoderCore.cs | 9 +- .../Formats/Icon/IconFrameMetadata.cs | 104 ------------------ 6 files changed, 140 insertions(+), 148 deletions(-) delete mode 100644 src/ImageSharp/Formats/Icon/IconFrameMetadata.cs diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index 44fcc8fcc..afd751279 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -14,5 +14,5 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata(); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) => metadata.GetCurMetadata().FromIconDirEntry(entry); } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs index 60ced16ea..2d1c22776 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -4,9 +4,9 @@ namespace SixLabors.ImageSharp.Formats.Icon.Cur; /// -/// IcoFrameMetadata. TODO: Remove base class and merge into this class. +/// IcoFrameMetadata. /// -public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { /// /// Initializes a new instance of the class. @@ -15,38 +15,92 @@ public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Initializes a new instance of the class. - /// - /// metadata - public CurFrameMetadata(IconFrameMetadata metadata) - : base(metadata) - { - } - /// /// Initializes a new instance of the class. /// /// width /// height /// colorCount - /// field1 - /// field2 - public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - : base(width, height, colorCount, field1, field2) + /// hotspotX + /// hotspotY + public CurFrameMetadata(byte width, byte height, byte colorCount, ushort hotspotX, ushort hotspotY) { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; + this.HotspotX = hotspotX; + this.HotspotY = hotspotY; } + /// + public CurFrameMetadata(CurFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.HotspotX = metadata.HotspotX; + this.HotspotY = metadata.HotspotY; + this.Compression = metadata.Compression; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets ColorCount field.
    + /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
    + // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + /// /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. /// - public ushort HotspotX { get => this.Field1; set => this.Field1 = value; } + public ushort HotspotX { get; set; } /// /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. /// - public ushort HotspotY { get => this.Field2; set => this.Field2 = value; } + public ushort HotspotY { get; set; } + + /// + /// Gets or sets Height field.
    + /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + ///
    + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
    + /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
    + public byte EncodingWidth { get; set; } + + /// + public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; /// - public override CurFrameMetadata DeepClone() => new(this); + public CurFrameMetadata DeepClone() => new(this); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry entry) + { + this.EncodingWidth = entry.Width; + this.EncodingHeight = entry.Height; + this.ColorCount = entry.ColorCount; + this.HotspotX = entry.Planes; + this.HotspotY = entry.BitCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = this.HotspotX, + BitCount = this.HotspotY, + }; } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 677a9c07a..4884fdf9a 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -14,5 +14,6 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetIcoMetadata(); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) + => metadata.GetIcoMetadata().FromIconDirEntry(entry); } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs index 4f557715d..82e3c1db3 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Ico; /// /// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// -public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { /// /// Initializes a new instance of the class. @@ -15,34 +15,78 @@ public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Initializes a new instance of the class. - /// - /// metadata - public IcoFrameMetadata(IconFrameMetadata metadata) - : base(metadata) - { - } - /// /// Initializes a new instance of the class. /// /// width /// height /// colorCount - /// field1 - /// field2 - public IcoFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - : base(width, height, colorCount, field1, field2) + public IcoFrameMetadata(byte width, byte height, byte colorCount) { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; } + /// + public IcoFrameMetadata(IcoFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.Compression = metadata.Compression; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets ColorCount field.
    + /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
    + // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + /// - /// Gets or sets the bits per pixel. - /// TODO: This needs to be constrained and calculated using the metadata returned from the png/bmp. + /// Gets or sets Height field.
    + /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. ///
    - public ushort BitCount { get => this.Field2; set => this.Field2 = value; } + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
    + /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
    + public byte EncodingWidth { get; set; } + + /// + public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; /// - public override IcoFrameMetadata DeepClone() => new(this); + public IcoFrameMetadata DeepClone() => new(this); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry entry) + { + this.EncodingWidth = entry.Width; + this.EncodingHeight = entry.Height; + this.ColorCount = entry.ColorCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = 1, + BitCount = this.Compression switch + { + IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, + _ => 0, + }, + }; } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 1077bde51..21c0a3bc4 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -93,9 +93,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals bmpMetadata = x.Image.Metadata.GetBmpMetadata(); } - // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) - // so we can avoid the protected Field1 and Field2 properties and use strong typing. - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[x.Index]); + this.SetFrameMetadata(target.Metadata, this.Entries[x.Index]); x.Image.Dispose(); @@ -127,15 +125,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // TODO: Use the Identify methods in each decoder to return the // format specific metadata for the image and frame. frames[i] = new(); - IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); - icoFrameMetadata.FromIconDirEntry(this.Entries[i]); + this.SetFrameMetadata(frames[i], this.Entries[i]); } // TODO: Use real values from the metadata. return new(new(32), new(0), metadata, frames); } - protected abstract IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata); + protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry); protected void ReadHeader(Stream stream) { diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs deleted file mode 100644 index 5c23388e7..000000000 --- a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Icon; - -/// -/// IconFrameMetadata -/// TODO: Delete this. Treat the individual metadata types as separate types so we can avoid Field1, Field2 and use strong typing with constaints. -/// -public abstract class IconFrameMetadata : IDeepCloneable -{ - /// - /// Initializes a new instance of the class. - /// - protected IconFrameMetadata() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - /// field1 - /// field2 - protected IconFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - this.Field1 = field1; - this.Field2 = field2; - } - - /// - protected IconFrameMetadata(IconFrameMetadata metadata) - { - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; - this.Field1 = metadata.Field1; - this.Field2 = metadata.Field2; - } - - /// - /// Gets or sets icoFrameCompression. - /// - public IconFrameCompression Compression { get; protected set; } - - /// - /// Gets or sets ColorCount field.
    - /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. - ///
    - // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } - - /// - /// Gets or sets Planes field.
    - /// In ICO format: Specifies color planes. Should be 0 or 1.
    - /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. - ///
    - protected ushort Field1 { get; set; } - - /// - /// Gets or sets BitCount field.
    - /// In ICO format: Specifies bits per pixel.
    - /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. - ///
    - protected ushort Field2 { get; set; } - - /// - /// Gets or sets Height field.
    - /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. - ///
    - public byte EncodingHeight { get; set; } - - /// - /// Gets or sets Width field.
    - /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. - ///
    - public byte EncodingWidth { get; set; } - - /// - public abstract IDeepCloneable DeepClone(); - - internal void FromIconDirEntry(in IconDirEntry metadata) - { - this.EncodingWidth = metadata.Width; - this.EncodingHeight = metadata.Height; - this.ColorCount = metadata.ColorCount; - this.Field1 = metadata.Planes; - this.Field2 = metadata.BitCount; - } - - internal IconDirEntry ToIconDirEntry() => new() - { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = this.Field1, - BitCount = this.Field2, - }; -} From 729d64ec757536c30c3f3ed5255f2460762206d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:56 +0800 Subject: [PATCH 164/220] Refactor SetFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderCore.cs | 8 ++- .../Formats/Icon/Ico/IcoDecoderCore.cs | 9 ++- .../Formats/Icon/IconDecoderCore.cs | 64 +++++++++++++------ 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index afd751279..2ed89483a 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -14,5 +14,11 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) => metadata.GetCurMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); + curFrameMetadata.FromIconDirEntry(entry); + curFrameMetadata.Compression = compression; + curFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 4884fdf9a..c7841ce39 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -14,6 +14,11 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) - => metadata.GetIcoMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); + icoFrameMetadata.FromIconDirEntry(entry); + icoFrameMetadata.Compression = compression; + icoFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 21c0a3bc4..6af9553cc 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -33,7 +33,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { @@ -58,7 +58,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); - decodedEntries.Add((temp, isPng, i)); + decodedEntries.Add((temp, isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, i)); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. @@ -66,10 +66,10 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } ImageMetadata metadata = new(); - BmpMetadata? bmpMetadata = null; PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { + BmpBitsPerPixel bitsPerPixel = default; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -78,7 +78,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Copy the format specific frame metadata to the image. - if (x.IsPng) + if (x.Compression is IconFrameCompression.Png) { if (x.Index == 0) { @@ -88,12 +88,12 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else if (x.Index == 0) + else { - bmpMetadata = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(target.Metadata, this.Entries[x.Index]); + this.SetFrameMetadata(target.Metadata, this.Entries[x.Index], x.Compression, bitsPerPixel); x.Image.Dispose(); @@ -101,11 +101,6 @@ internal abstract class IconDecoderCore : IImageDecoderInternals }).ToArray()); // Copy the format specific metadata to the image. - if (bmpMetadata != null) - { - result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); - } - if (pngMetadata != null) { result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); @@ -116,23 +111,56 @@ internal abstract class IconDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { + // Stream may not at 0. + long basePosition = stream.Position; this.ReadHeader(stream); + Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; + ImageMetadata metadata = new(); ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; for (int i = 0; i < frames.Length; i++) { - // TODO: Use the Identify methods in each decoder to return the - // format specific metadata for the image and frame. + BmpBitsPerPixel bitsPerPixel = default; + ref IconDirEntry entry = ref this.Entries[i]; + + // If we hit the end of the stream we should break. + if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) + { + break; + } + + // There should always be enough bytes for this regardless of the entry type. + if (stream.Read(flag) != PngConstants.HeaderBytes.Length) + { + break; + } + + // Reset the stream position. + stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); + + // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. + ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); + frames[i] = new(); - this.SetFrameMetadata(frames[i], this.Entries[i]); + if (isPng) + { + bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; + } + + this.SetFrameMetadata(frames[i], this.Entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. + this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } - // TODO: Use real values from the metadata. - return new(new(32), new(0), metadata, frames); + return new(new(32), this.Dimensions, metadata, frames); } - protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry); + protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); protected void ReadHeader(Stream stream) { From 63da967c7f8e41467243993ef64cc45079f46b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:57 +0800 Subject: [PATCH 165/220] Flatten namespace. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Configuration.cs | 4 ++-- .../Formats/{Icon => }/Cur/CurConfigurationModule.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Cur/CurConstants.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurDecoder.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurDecoderCore.cs | 5 ++--- src/ImageSharp/Formats/{Icon => }/Cur/CurFormat.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurFrameMetadata.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Cur/CurMetadata.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/MetadataExtensions.cs | 2 +- .../Formats/{Icon => }/Ico/IcoConfigurationModule.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Ico/IcoConstants.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoder.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoderCore.cs | 5 ++--- src/ImageSharp/Formats/{Icon => }/Ico/IcoFormat.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoFrameMetadata.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Ico/IcoMetadata.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/MetadataExtensions.cs | 2 +- tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs | 4 +--- tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs | 2 +- 19 files changed, 30 insertions(+), 26 deletions(-) rename src/ImageSharp/Formats/{Icon => }/Cur/CurConfigurationModule.cs (88%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurConstants.cs (93%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurDecoder.cs (96%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurDecoderCore.cs (84%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurFormat.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurFrameMetadata.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurMetadata.cs (89%) rename src/ImageSharp/Formats/{Icon => }/Cur/MetadataExtensions.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoConfigurationModule.cs (88%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoConstants.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoder.cs (96%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoderCore.cs (84%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoFormat.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoFrameMetadata.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoMetadata.cs (89%) rename src/ImageSharp/Formats/{Icon => }/Ico/MetadataExtensions.cs (97%) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index d6cfd480d..1d9f3bb85 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -4,9 +4,9 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Icon.Cur; -using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs similarity index 88% rename from src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs rename to src/ImageSharp/Formats/Cur/CurConfigurationModule.cs index c975bc609..1c7db4bab 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs +++ b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Registers the image encoders, decoders and mime type detectors for the Ico format. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs b/src/ImageSharp/Formats/Cur/CurConstants.cs similarity index 93% rename from src/ImageSharp/Formats/Icon/Cur/CurConstants.cs rename to src/ImageSharp/Formats/Cur/CurConstants.cs index 701b40cf4..6efd2817c 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs +++ b/src/ImageSharp/Formats/Cur/CurConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Defines constants relating to ICOs diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs b/src/ImageSharp/Formats/Cur/CurDecoder.cs similarity index 96% rename from src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs rename to src/ImageSharp/Formats/Cur/CurDecoder.cs index ceefdcaba..cbe646c47 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoder.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Decoder for generating an image out of a ico encoded stream. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs similarity index 84% rename from src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs rename to src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 2ed89483a..4c0224652 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -1,11 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; -// TODO: flatten namespace. -// namespace SixLabors.ImageSharp.Formats.Cur; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; internal sealed class CurDecoderCore : IconDecoderCore { diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs b/src/ImageSharp/Formats/Cur/CurFormat.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Cur/CurFormat.cs rename to src/ImageSharp/Formats/Cur/CurFormat.cs index 1e5758bc4..af93382ec 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs +++ b/src/ImageSharp/Formats/Cur/CurFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Registers the image encoders, decoders and mime type detectors for the ICO format. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs rename to src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index 2d1c22776..e8f3cfe8e 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; /// /// IcoFrameMetadata. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs b/src/ImageSharp/Formats/Cur/CurMetadata.cs similarity index 89% rename from src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs rename to src/ImageSharp/Formats/Cur/CurMetadata.cs index ed3c322b4..5c3486d4a 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurMetadata.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Provides Ico specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs rename to src/ImageSharp/Formats/Cur/MetadataExtensions.cs index 400ece648..2b410a0f9 100644 --- a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Extension methods for the type. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs similarity index 88% rename from src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs rename to src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs index 7074189c7..b27d91465 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs +++ b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Registers the image encoders, decoders and mime type detectors for the Ico format. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Ico/IcoConstants.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs rename to src/ImageSharp/Formats/Ico/IcoConstants.cs index 345711705..0b963a431 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs +++ b/src/ImageSharp/Formats/Ico/IcoConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Defines constants relating to ICOs diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs b/src/ImageSharp/Formats/Ico/IcoDecoder.cs similarity index 96% rename from src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs rename to src/ImageSharp/Formats/Ico/IcoDecoder.cs index 5d6137920..a0c8a3075 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoder.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Decoder for generating an image out of a ico encoded stream. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs similarity index 84% rename from src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs rename to src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index c7841ce39..859340fb4 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -1,11 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; -// TODO: flatten namespace. -// namespace SixLabors.ImageSharp.Formats.Ico; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; internal sealed class IcoDecoderCore : IconDecoderCore { diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs b/src/ImageSharp/Formats/Ico/IcoFormat.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs rename to src/ImageSharp/Formats/Ico/IcoFormat.cs index 27b0525bf..5f89ab7f2 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs +++ b/src/ImageSharp/Formats/Ico/IcoFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Registers the image encoders, decoders and mime type detectors for the ICO format. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs rename to src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 82e3c1db3..2a275d1a1 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; /// /// IcoFrameMetadata. TODO: Remove base class and merge into this class. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs b/src/ImageSharp/Formats/Ico/IcoMetadata.cs similarity index 89% rename from src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs rename to src/ImageSharp/Formats/Ico/IcoMetadata.cs index b227d0bd6..f165bf916 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoMetadata.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Provides Ico specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs rename to src/ImageSharp/Formats/Ico/MetadataExtensions.cs index fb5b4afe7..ba21d0abc 100644 --- a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Extension methods for the type. diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index f78c04cdf..d1587d35b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -19,8 +19,6 @@ public class CurDecoderTests image.DebugSaveMultiFrame(provider, extension: "png"); - image.DebugSaveMultiFrame(provider, extension: "png"); - // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 86f8b003b..56378653a 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; From 794b2a6433cad5183901bd22e0c08c26126f9110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:58 +0800 Subject: [PATCH 166/220] Refactor IconImageFormatDetector. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconImageFormatDetector.cs | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index aff8dfe0a..4fceef3b8 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Icon; @@ -12,22 +11,58 @@ namespace SixLabors.ImageSharp.Formats.Icon; public class IconImageFormatDetector : IImageFormatDetector { /// - public int HeaderSize { get; } = 4; + public int HeaderSize { get; } = IconDir.Size + IconDirEntry.Size; /// public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) { - switch (MemoryMarshal.Cast(header)[0]) + format = this.IsSupportedFileFormat(header) switch { - case Ico.IcoConstants.FileHeader: - format = Ico.IcoFormat.Instance; - return true; - case Cur.CurConstants.FileHeader: - format = Cur.CurFormat.Instance; - return true; - default: - format = default; - return false; + true => Ico.IcoFormat.Instance, + false => Cur.CurFormat.Instance, + null => default + }; + + return format is not null; + } + + private bool? IsSupportedFileFormat(ReadOnlySpan header) + { + // There are no magic bytes in the first few bytes of a tga file, + // so we try to figure out if its a valid tga by checking for valid tga header bytes. + if (header.Length < this.HeaderSize) + { + return null; + } + + IconDir dir = IconDir.Parse(header); + if (dir is not { Reserved: 0 } // Should be 0. + or not { Type: IconFileType.ICO or IconFileType.CUR } // Unknown Type. + or { Count: 0 }) // Should not be 0. + { + return null; + } + + IconDirEntry entry = IconDirEntry.Parse(header[IconDir.Size..]); + if (entry is not { Reserved: 0 } // Should be 0. + or { BytesInRes: 0 } // Should not be 0. + || entry.ImageOffset < IconDir.Size + (dir.Count * IconDirEntry.Size)) + { + return null; + } + + if (dir.Type is IconFileType.ICO) + { + if (entry is not { BitCount: 1 or 4 or 8 or 16 or 24 or 32 } or not { Planes: 0 or 1 }) + { + return null; + } + + return true; + } + else + { + return false; } } } From 746e742183e671d83c42219a69ff9ea578daeeb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:32:56 +0800 Subject: [PATCH 167/220] Fixed errors and warnings --- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 7 +------ src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 7 +------ .../Formats/Icon/IconDecoderCore.cs | 20 +++++++++---------- src/ImageSharp/Formats/Icon/IconDir.cs | 15 ++++---------- 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 4c0224652..538f9a2c6 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -6,13 +6,8 @@ using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurDecoderCore : IconDecoderCore +internal sealed class CurDecoderCore(DecoderOptions options) : IconDecoderCore(options) { - public CurDecoderCore(DecoderOptions options) - : base(options) - { - } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index 859340fb4..78cb0d961 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -6,13 +6,8 @@ using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoDecoderCore : IconDecoderCore +internal sealed class IcoDecoderCore(DecoderOptions options) : IconDecoderCore(options) { - public IcoDecoderCore(DecoderOptions options) - : base(options) - { - } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 6af9553cc..983d4c346 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; @@ -10,19 +9,17 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconDecoderCore : IImageDecoderInternals +internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals { private IconDir fileHeader; - public IconDecoderCore(DecoderOptions options) => this.Options = options; - - public DecoderOptions Options { get; } + public DecoderOptions Options { get; } = options; public Size Dimensions { get; private set; } protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } - protected IconDirEntry[] Entries { get; private set; } = Array.Empty(); + protected IconDirEntry[] Entries { get; private set; } = []; public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel @@ -52,7 +49,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Reset the stream position. - stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + _ = stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); @@ -86,7 +83,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Bmp does not contain frame specific metadata. - target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); + target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngMetadata()); } else { @@ -137,7 +134,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Reset the stream position. - stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + _ = stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); @@ -210,7 +207,10 @@ internal abstract class IconDecoderCore : IImageDecoderInternals { if (isPng) { - return new PngDecoderCore(this.Options); + return new PngDecoderCore(new() + { + GeneralOptions = this.Options, + }); } else { diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index 53b87bc9f..390a4de65 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -6,12 +6,12 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Icon; [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] -internal struct IconDir +internal struct IconDir(ushort reserved, IconFileType type, ushort count) { public const int Size = 3 * sizeof(ushort); - public ushort Reserved; - public IconFileType Type; - public ushort Count; + public ushort Reserved = reserved; + public IconFileType Type = type; + public ushort Count = count; public IconDir(IconFileType type) : this(type, 0) @@ -23,13 +23,6 @@ internal struct IconDir { } - public IconDir(ushort reserved, IconFileType type, ushort count) - { - this.Reserved = reserved; - this.Type = type; - this.Count = count; - } - public static IconDir Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; } From dbd178392f21d3cf36d2bb2129193453acb58fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 17:34:38 +0800 Subject: [PATCH 168/220] Add encoders that cannot be used. --- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 9 +++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 81 ++++++++++++++++++- .../Formats/Cur/CurConfigurationModule.cs | 3 +- src/ImageSharp/Formats/Cur/CurEncoder.cs | 17 ++++ src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 19 +++++ .../Formats/Ico/IcoConfigurationModule.cs | 3 +- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 17 ++++ src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 19 +++++ src/ImageSharp/Formats/Icon/IconAssert.cs | 14 ++++ src/ImageSharp/Formats/Icon/IconDir.cs | 3 + src/ImageSharp/Formats/Icon/IconDirEntry.cs | 3 + .../Formats/Icon/IconEncoderCore.cs | 69 ++++++++++++++++ src/ImageSharp/Metadata/ImageMetadata.cs | 1 + .../Formats/Icon/Cur/CurEncoderTests.cs | 32 ++++++++ .../Formats/Icon/Ico/IcoEncoderTests.cs | 32 ++++++++ 15 files changed, 316 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/Formats/Cur/CurEncoder.cs create mode 100644 src/ImageSharp/Formats/Cur/CurEncoderCore.cs create mode 100644 src/ImageSharp/Formats/Ico/IcoEncoder.cs create mode 100644 src/ImageSharp/Formats/Ico/IcoEncoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/IconEncoderCore.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 0081f6a1a..0be243f9a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -29,6 +29,15 @@ public sealed class BmpEncoder : QuantizingImageEncoder /// public bool SupportTransparency { get; init; } + /// + internal bool ProcessedAlphaMask { get; init; } + + /// + internal bool SkipFileHeader { get; init; } + + /// + internal bool UseDoubleHeight { get; init; } + /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 076d1adf0..b888fa400 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Binary; +using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; @@ -11,6 +12,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; +using static System.Net.Mime.MediaTypeNames; namespace SixLabors.ImageSharp.Formats.Bmp; @@ -92,6 +94,15 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// private readonly IPixelSamplingStrategy pixelSamplingStrategy; + /// + private readonly bool processedAlphaMask; + + /// + private readonly bool skipFileHeader; + + /// + private readonly bool isDoubleHeight; + /// /// Initializes a new instance of the class. /// @@ -104,6 +115,9 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = encoder.PixelSamplingStrategy; this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3; + this.processedAlphaMask = encoder.ProcessedAlphaMask; + this.skipFileHeader = encoder.SkipFileHeader; + this.isDoubleHeight = encoder.UseDoubleHeight; } /// @@ -154,11 +168,23 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals _ => BmpInfoHeader.SizeV3 }; - BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, image.Height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData); + // for ico/cur encoder. + int height = image.Height; + if (this.isDoubleHeight) + { + height <<= 1; + } + + BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData); Span buffer = stackalloc byte[infoHeaderSize]; - WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); + // for ico/cur encoder. + if (!this.skipFileHeader) + { + WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); + } + this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); this.WriteImage(configuration, stream, image); WriteColorProfile(stream, iccProfileData, buffer); @@ -455,6 +481,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.Write8BitColor(configuration, stream, image, colorPalette); } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -572,6 +603,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -629,6 +665,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -679,6 +720,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -722,4 +768,35 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(indices); } + + private static void ProcessedAlphaMask(Stream stream, Image image) + where TPixel : unmanaged, IPixel + { + Rgba32 rgba = default; + int arrayWidth = image.Width / 8; + int padding = arrayWidth % 4; + if (padding is not 0) + { + padding = 4 - padding; + } + + Span mask = stackalloc byte[arrayWidth]; + for (int y = image.Height - 1; y >= 0; y--) + { + mask.Clear(); + for (int x = 0; x < image.Width; x++) + { + int bit = x % 8; + int i = x / 8; + TPixel pixel = image[x, y]; + pixel.ToRgba32(ref rgba); + if (rgba.A is not 0) + { + mask[i] &= unchecked((byte)(0b10000000 >> bit)); + } + } + + stream.Write(mask); + } + } } diff --git a/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs index 1c7db4bab..879b3f112 100644 --- a/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs +++ b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs @@ -13,8 +13,7 @@ public sealed class CurConfigurationModule : IImageFormatConfigurationModule /// public void Configure(Configuration configuration) { - // TODO: CurEncoder - // configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); + configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Cur/CurEncoder.cs b/src/ImageSharp/Formats/Cur/CurEncoder.cs new file mode 100644 index 000000000..d237fe7d0 --- /dev/null +++ b/src/ImageSharp/Formats/Cur/CurEncoder.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Cur; + +/// +/// Image encoder for writing an image to a stream as a Windows Cursor. +/// +public sealed class CurEncoder : QuantizingImageEncoder +{ + /// + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) + { + CurEncoderCore encoderCore = new(); + encoderCore.Encode(image, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs new file mode 100644 index 000000000..0cf8c97a5 --- /dev/null +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; + +internal sealed class CurEncoderCore : IconEncoderCore +{ + protected override void GetHeader(in Image image) + { + this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); + this.Entries = image.Frames.Select(i => + { + CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); + return metadata.ToIconDirEntry(); + }).ToArray(); + } +} diff --git a/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs index b27d91465..224aaa31e 100644 --- a/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs +++ b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs @@ -13,8 +13,7 @@ public sealed class IcoConfigurationModule : IImageFormatConfigurationModule /// public void Configure(Configuration configuration) { - // TODO: IcoEncoder - // configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); + configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs new file mode 100644 index 000000000..0668a7e23 --- /dev/null +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Ico; + +/// +/// Image encoder for writing an image to a stream as a Windows Icon. +/// +public sealed class IcoEncoder : QuantizingImageEncoder +{ + /// + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) + { + IcoEncoderCore encoderCore = new(); + encoderCore.Encode(image, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs new file mode 100644 index 000000000..6c2f3abe4 --- /dev/null +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; + +internal sealed class IcoEncoderCore : IconEncoderCore +{ + protected override void GetHeader(in Image image) + { + this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); + this.Entries = image.Frames.Select(i => + { + IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); + return metadata.ToIconDirEntry(); + }).ToArray(); + } +} diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index 04a9527b9..547b6a6eb 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -32,4 +32,18 @@ internal class IconAssert return v; } + + internal static byte Is1ByteSize(int i) + { + if (i is 256) + { + return 0; + } + else if (i > byte.MaxValue) + { + throw new FormatException("Image size Too Large."); + } + + return (byte)i; + } } diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index 390a4de65..aa583ee1e 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -25,4 +25,7 @@ internal struct IconDir(ushort reserved, IconFileType type, ushort count) public static IconDir Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; + + public unsafe void WriteTo(in Stream stream) + => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index edd778f7e..7a8e09e37 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -28,4 +28,7 @@ internal struct IconDirEntry public static IconDirEntry Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; + + public unsafe void WriteTo(in Stream stream) + => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs new file mode 100644 index 000000000..05a657b51 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal abstract class IconEncoderCore : IImageEncoderInternals +{ + protected IconDir FileHeader { get; set; } + + protected IconDirEntry[]? Entries { get; set; } + + public void Encode( + Image image, + Stream stream, + CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + Guard.NotNull(image, nameof(image)); + Guard.NotNull(stream, nameof(stream)); + + IconAssert.CanSeek(stream); + + // Stream may not at 0. + long basePosition = stream.Position; + this.GetHeader(image); + + int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length); + _ = stream.Seek(dataOffset, SeekOrigin.Current); + + for (int i = 0; i < image.Frames.Count; i++) + { + ImageFrame frame = image.Frames[i]; + this.Entries[i].ImageOffset = (uint)stream.Position; + Image img = new(Configuration.Default, frame.PixelBuffer, new()); + + // Note: this encoder are not supported PNG Data. + BmpEncoder encoder = new() + { + ProcessedAlphaMask = true, + UseDoubleHeight = true, + SkipFileHeader = true, + SupportTransparency = false, + BitsPerPixel = this.Entries[i].BitCount is 0 + ? BmpBitsPerPixel.Pixel8 + : (BmpBitsPerPixel?)this.Entries[i].BitCount + }; + + encoder.Encode(img, stream); + this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position; + } + + long endPosition = stream.Position; + _ = stream.Seek(basePosition, SeekOrigin.Begin); + this.FileHeader.WriteTo(stream); + foreach (IconDirEntry entry in this.Entries) + { + entry.WriteTo(stream); + } + + _ = stream.Seek(endPosition, SeekOrigin.Begin); + } + + [MemberNotNull(nameof(Entries))] + protected abstract void GetHeader(in Image image); +} diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index d9aba6631..e811cc1f7 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -215,6 +215,7 @@ public sealed class ImageMetadata : IDeepCloneable metadata = default; return false; } + internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) where TFormatMetadata : class, IDeepCloneable => this.formatMetadata[key] = value; diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs new file mode 100644 index 000000000..9908786f1 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Cur; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; + +[Trait("Format", "Cur")] +public class CurEncoderTests +{ + private static CurEncoder CurEncoder => new(); + + public static readonly TheoryData Files = new() + { + { WindowsMouse }, + }; + + [Theory] + [MemberData(nameof(Files))] + public void Encode(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, CurEncoder); + + memStream.Seek(0, SeekOrigin.Begin); + CurDecoder.Instance.Decode(new(), memStream); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs new file mode 100644 index 000000000..9a239bdd4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Ico; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; + +[Trait("Format", "Icon")] +public class IcoEncoderTests +{ + private static IcoEncoder CurEncoder => new(); + + public static readonly TheoryData Files = new() + { + { Flutter }, + }; + + [Theory] + [MemberData(nameof(Files))] + public void Encode(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, CurEncoder); + + memStream.Seek(0, SeekOrigin.Begin); + IcoDecoder.Instance.Decode(new(), memStream); + } +} From 54e87ab63ace1daa546f0476408fb84ab1557967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 19:43:12 +0800 Subject: [PATCH 169/220] Remove a comment --- src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index 4fceef3b8..b66b6c79f 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -38,7 +38,7 @@ public class IconImageFormatDetector : IImageFormatDetector IconDir dir = IconDir.Parse(header); if (dir is not { Reserved: 0 } // Should be 0. or not { Type: IconFileType.ICO or IconFileType.CUR } // Unknown Type. - or { Count: 0 }) // Should not be 0. + or { Count: 0 }) { return null; } From 21b1e71062915603e16792b88e5cb528d5444d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 21:47:27 +0800 Subject: [PATCH 170/220] Encoder Worked! but AlphaMask have some problems --- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 11 +-- .../Formats/Cur/MetadataExtensions.cs | 5 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 11 +-- .../Formats/Ico/IcoFrameMetadata.cs | 3 +- .../Formats/Ico/MetadataExtensions.cs | 5 +- .../Formats/Icon/IconEncoderCore.cs | 97 ++++++++++++++----- 6 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index 0cf8c97a5..3a7288b71 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurEncoderCore : IconEncoderCore +internal sealed class CurEncoderCore() : IconEncoderCore(IconFileType.CUR) { - protected override void GetHeader(in Image image) - { - this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); - this.Entries = image.Frames.Select(i => - { - CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return metadata.ToIconDirEntry(); - }).ToArray(); - } } diff --git a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs index 2b410a0f9..6394c564b 100644 --- a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs @@ -2,14 +2,15 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Cur; +namespace SixLabors.ImageSharp; /// /// Extension methods for the type. /// -public static class MetadataExtensions +public static partial class MetadataExtensions { /// /// Gets the Icon format specific metadata for the image. diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index 6c2f3abe4..12ced58fd 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoEncoderCore : IconEncoderCore +internal sealed class IcoEncoderCore() : IconEncoderCore(IconFileType.ICO) { - protected override void GetHeader(in Image image) - { - this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); - this.Entries = image.Frames.Select(i => - { - IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return metadata.ToIconDirEntry(); - }).ToArray(); - } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 2a275d1a1..8d7eb17b5 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -88,7 +88,8 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable BitCount = this.Compression switch { IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, - _ => 0, + IconFrameCompression.Png => 32, + _ => throw new NotSupportedException($"Value: {this.Compression}"), }, }; } diff --git a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs index ba21d0abc..497375f99 100644 --- a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs @@ -2,14 +2,15 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Ico; +namespace SixLabors.ImageSharp; /// /// Extension methods for the type. /// -public static class MetadataExtensions +public static partial class MetadataExtensions { /// /// Gets the Ico format specific metadata for the image. diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 05a657b51..b4d563d4b 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -2,16 +2,18 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconEncoderCore : IImageEncoderInternals +internal abstract class IconEncoderCore(IconFileType iconFileType) + : IImageEncoderInternals { - protected IconDir FileHeader { get; set; } + private IconDir fileHeader; - protected IconDirEntry[]? Entries { get; set; } + private IconFrameMetadata[]? entries; public void Encode( Image image, @@ -26,44 +28,93 @@ internal abstract class IconEncoderCore : IImageEncoderInternals // Stream may not at 0. long basePosition = stream.Position; - this.GetHeader(image); + this.InitHeader(image); - int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length); + int dataOffset = IconDir.Size + (IconDirEntry.Size * this.entries.Length); _ = stream.Seek(dataOffset, SeekOrigin.Current); for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; - this.Entries[i].ImageOffset = (uint)stream.Position; - Image img = new(Configuration.Default, frame.PixelBuffer, new()); + int width = this.entries[i].Entry.Width; + if (width is 0) + { + width = frame.Width; + } + + int height = this.entries[i].Entry.Height; + if (height is 0) + { + height = frame.Height; + } + + this.entries[i].Entry.ImageOffset = (uint)stream.Position; - // Note: this encoder are not supported PNG Data. - BmpEncoder encoder = new() + Image img = new(width, height); + for (int y = 0; y < height; y++) { - ProcessedAlphaMask = true, - UseDoubleHeight = true, - SkipFileHeader = true, - SupportTransparency = false, - BitsPerPixel = this.Entries[i].BitCount is 0 - ? BmpBitsPerPixel.Pixel8 - : (BmpBitsPerPixel?)this.Entries[i].BitCount + frame.PixelBuffer.DangerousGetRowSpan(y)[..width].CopyTo(img.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); + } + + QuantizingImageEncoder encoder = this.entries[i].Compression switch + { + IconFrameCompression.Bmp => new Bmp.BmpEncoder() + { + ProcessedAlphaMask = true, + UseDoubleHeight = true, + SkipFileHeader = true, + SupportTransparency = false, + BitsPerPixel = iconFileType is IconFileType.ICO + ? (Bmp.BmpBitsPerPixel?)this.entries[i].Entry.BitCount + : Bmp.BmpBitsPerPixel.Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image + }, + IconFrameCompression.Png => new Png.PngEncoder(), + _ => throw new NotSupportedException(), }; encoder.Encode(img, stream); - this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position; + this.entries[i].Entry.BytesInRes = (uint)stream.Position - this.entries[i].Entry.ImageOffset; } long endPosition = stream.Position; _ = stream.Seek(basePosition, SeekOrigin.Begin); - this.FileHeader.WriteTo(stream); - foreach (IconDirEntry entry in this.Entries) + this.fileHeader.WriteTo(stream); + foreach (IconFrameMetadata frame in this.entries) { - entry.WriteTo(stream); + frame.Entry.WriteTo(stream); } _ = stream.Seek(endPosition, SeekOrigin.Begin); } - [MemberNotNull(nameof(Entries))] - protected abstract void GetHeader(in Image image); + [MemberNotNull(nameof(entries))] + private void InitHeader(in Image image) + { + this.fileHeader = new(iconFileType, (ushort)image.Frames.Count); + this.entries = iconFileType switch + { + IconFileType.ICO => + image.Frames.Select(i => + { + IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); + return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + }).ToArray(), + IconFileType.CUR => + image.Frames.Select(i => + { + CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); + return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + }).ToArray(), + _ => throw new NotSupportedException(), + }; + } + + internal sealed class IconFrameMetadata(IconFrameCompression compression, IconDirEntry iconDirEntry) + { + private IconDirEntry iconDirEntry = iconDirEntry; + + public IconFrameCompression Compression { get; set; } = compression; + + public ref IconDirEntry Entry => ref this.iconDirEntry; + } } From e252a6f223120357fe00cc9c3aa842d250b131ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 21:48:34 +0800 Subject: [PATCH 171/220] Using Seek requires consideration of whether this stream allows Seek and the case where the stream start position is not 0. --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index b888fa400..dcccc8363 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -133,6 +133,9 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); + // Stream may not at 0. + long basePosition = stream.Position; + Configuration configuration = image.Configuration; ImageMetadata metadata = image.Metadata; BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); @@ -187,7 +190,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); this.WriteImage(configuration, stream, image); - WriteColorProfile(stream, iccProfileData, buffer); + WriteColorProfile(stream, iccProfileData, buffer, basePosition); stream.Flush(); } @@ -271,16 +274,20 @@ 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) + /// The Stream may not be start with 0. + private static void WriteColorProfile(Stream stream, byte[]? iccProfileData, Span buffer, long basePosition) { if (iccProfileData != null) { // The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data. int streamPositionAfterImageData = (int)stream.Position - BmpFileHeader.Size; stream.Write(iccProfileData); + long position = stream.Position; // Storage Position BinaryPrimitives.WriteInt32LittleEndian(buffer, streamPositionAfterImageData); - stream.Position = BmpFileHeader.Size + 112; + _ = stream.Seek(basePosition, SeekOrigin.Begin); + _ = stream.Seek(BmpFileHeader.Size + 112, SeekOrigin.Current); stream.Write(buffer[..4]); + _ = stream.Seek(position, SeekOrigin.Begin); // Reset Position } } From fae15ae88aa1b8131076159b199f51c1d32ac70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 22:51:52 +0800 Subject: [PATCH 172/220] Fixed AlphaMask --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 55 +++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index dcccc8363..fe1c9e0be 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -3,16 +3,13 @@ using System.Buffers; using System.Buffers.Binary; -using System.IO; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using static System.Net.Mime.MediaTypeNames; namespace SixLabors.ImageSharp.Formats.Bmp; @@ -380,6 +377,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.Write1BitPixelData(configuration, stream, image); break; } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } private IMemoryOwner AllocateRow(int width, int bytesPerPixel) @@ -488,11 +490,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.Write8BitColor(configuration, stream, image, colorPalette); } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -610,11 +607,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -672,11 +664,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -727,11 +714,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -779,7 +761,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals private static void ProcessedAlphaMask(Stream stream, Image image) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; int arrayWidth = image.Width / 8; int padding = arrayWidth % 4; if (padding is not 0) @@ -791,19 +772,31 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals for (int y = image.Height - 1; y >= 0; y--) { mask.Clear(); - for (int x = 0; x < image.Width; x++) + Span row = image.GetRootFramePixelBuffer().DangerousGetRowSpan(y); + + for (int i = 0; i < arrayWidth; i++) { - int bit = x % 8; - int i = x / 8; - TPixel pixel = image[x, y]; - pixel.ToRgba32(ref rgba); - if (rgba.A is not 0) + int x = i * 8; + + for (int j = 0; j < 8; j++) { - mask[i] &= unchecked((byte)(0b10000000 >> bit)); + WriteAlphaMask(row[x + j], ref mask[i], j); } } stream.Write(mask); + stream.Skip(padding); + } + } + + private static void WriteAlphaMask(in TPixel pixel, ref byte mask, in int index) + where TPixel : unmanaged, IPixel + { + Rgba32 rgba = default; + pixel.ToRgba32(ref rgba); + if (rgba.A is 0) + { + mask |= unchecked((byte)(0b10000000 >> index)); } } } From 52f392db568bf27cbe127ae1812f6d05a9ab7df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 22:58:15 +0800 Subject: [PATCH 173/220] Fixed some warnings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconDecoderCore.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 983d4c346..a502b91c9 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; @@ -12,15 +13,12 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals { private IconDir fileHeader; + private IconDirEntry[]? entries; public DecoderOptions Options { get; } = options; public Size Dimensions { get; private set; } - protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } - - protected IconDirEntry[] Entries { get; private set; } = []; - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -30,11 +28,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.Entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.entries.Length); - for (int i = 0; i < this.Entries.Length; i++) + for (int i = 0; i < this.entries.Length; i++) { - ref IconDirEntry entry = ref this.Entries[i]; + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) @@ -90,7 +88,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(target.Metadata, this.Entries[x.Index], x.Compression, bitsPerPixel); + this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); x.Image.Dispose(); @@ -115,11 +113,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; ImageMetadata metadata = new(); - ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; + ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; for (int i = 0; i < frames.Length; i++) { BmpBitsPerPixel bitsPerPixel = default; - ref IconDirEntry entry = ref this.Entries[i]; + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) @@ -147,7 +145,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(frames[i], this.Entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. @@ -159,6 +157,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); + [MemberNotNull(nameof(entries))] protected void ReadHeader(Stream stream) { Span buffer = stackalloc byte[IconDirEntry.Size]; @@ -168,16 +167,16 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI this.fileHeader = IconDir.Parse(buffer); // ICONDIRENTRY - this.Entries = new IconDirEntry[this.FileHeader.Count]; - for (int i = 0; i < this.Entries.Length; i++) + this.entries = new IconDirEntry[this.fileHeader.Count]; + for (int i = 0; i < this.entries.Length; i++) { _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); - this.Entries[i] = IconDirEntry.Parse(buffer); + this.entries[i] = IconDirEntry.Parse(buffer); } int width = 0; int height = 0; - foreach (IconDirEntry entry in this.Entries) + foreach (IconDirEntry entry in this.entries) { // Since Windows 95 size of an image in the ICONDIRENTRY structure might // be set to zero, which means 256 pixels. From 5be5d64882e5e85fed199cbc621bcd16734b008b Mon Sep 17 00:00:00 2001 From: Poker Date: Thu, 14 Mar 2024 12:17:44 +0800 Subject: [PATCH 174/220] fix --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 4 ++-- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 568eea00a..11c6aef29 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -898,7 +898,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; - image[newY, newX].FromBgr24(Unsafe.As(ref colors[colorIndex])); + image[newY, newX] = Bgra32.FromBgr24(Unsafe.As(ref colors[colorIndex])); } offset++; @@ -942,7 +942,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int x = 0; x < width; x++) { - pixelRow[x].FromBgra32(image[newY, x]); + pixelRow[x] = TPixel.FromBgra32(image[newY, x]); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index fe1c9e0be..f84e4f9c2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -792,8 +792,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals private static void WriteAlphaMask(in TPixel pixel, ref byte mask, in int index) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; - pixel.ToRgba32(ref rgba); + Rgba32 rgba = pixel.ToRgba32(); if (rgba.A is 0) { mask |= unchecked((byte)(0b10000000 >> index)); From 2127b46a04a8147c9eb02ff362f3617466265891 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 May 2024 21:02:39 +1000 Subject: [PATCH 175/220] Implement PngFrameMetadata --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 10 +- .../Formats/Gif/GifFrameMetadata.cs | 13 +- src/ImageSharp/Formats/Gif/GifMetadata.cs | 8 +- .../Formats/Gif/MetadataExtensions.cs | 4 +- src/ImageSharp/Formats/IImageFormat.cs | 6 +- src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 97 ++++++++++- .../Formats/Png/Chunks/FrameControl.cs | 20 +-- .../Formats/Png/MetadataExtensions.cs | 14 +- src/ImageSharp/Formats/Png/PngBlendMethod.cs | 22 --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 41 +++-- .../Formats/Png/PngDisposalMethod.cs | 25 --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 17 +- .../Formats/Png/PngFrameMetadata.cs | 64 +++++-- src/ImageSharp/Formats/Png/PngMetadata.cs | 163 +++++++++++++++++- src/ImageSharp/Metadata/ImageMetadata.cs | 2 +- src/ImageSharp/PixelFormats/PixelColorType.cs | 31 ++-- .../Formats/Gif/GifEncoderTests.cs | 14 +- .../Formats/Gif/GifFrameMetadataTests.cs | 6 +- .../Formats/Gif/GifMetadataTests.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 20 +-- .../Formats/Png/PngFrameMetadataTests.cs | 19 +- .../Formats/WebP/WebpEncoderTests.cs | 14 +- .../ImageFrameCollectionTests.NonGeneric.cs | 2 +- .../Metadata/ImageFrameMetadataTests.cs | 4 +- .../PixelFormats/PixelColorTypeTests.cs | 39 +++++ 26 files changed, 475 insertions(+), 184 deletions(-) delete mode 100644 src/ImageSharp/Formats/Png/PngBlendMethod.cs delete mode 100644 src/ImageSharp/Formats/Png/PngDisposalMethod.cs diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index e0fe4973d..e110acc30 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -719,7 +719,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals gifMeta.HasTransparency = this.graphicsControlExtension.TransparencyFlag; gifMeta.TransparencyIndex = this.graphicsControlExtension.TransparencyIndex; gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime; - gifMeta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; + gifMeta.DisposalMode = this.graphicsControlExtension.DisposalMethod; } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index f4cc166fe..5fd20d6ae 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -171,7 +171,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals // Capture the global palette for reuse on subsequent frames and cleanup the quantized frame. TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray(); - this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMethod); + this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMode); stream.WriteByte(GifConstants.EndIntroducer); @@ -183,7 +183,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals { if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) { - return (GifMetadata)gif.DeepClone(); + return gif.DeepClone(); } if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) @@ -208,7 +208,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals GifFrameMetadata? metadata = null; if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) { - metadata = (GifFrameMetadata)gif.DeepClone(); + metadata = gif.DeepClone(); } else if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) { @@ -282,7 +282,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals previousDisposalMode); previousFrame = currentFrame; - previousDisposalMode = gifMetadata.DisposalMethod; + previousDisposalMode = gifMetadata.DisposalMode; } if (hasPaletteQuantizer) @@ -664,7 +664,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals bool hasTransparency = metadata.HasTransparency; byte packedValue = GifGraphicControlExtension.GetPackedValue( - disposalMode: metadata.DisposalMethod, + disposalMode: metadata.DisposalMode, transparencyFlag: hasTransparency); GifGraphicControlExtension extension = new( diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs index 46bc415ce..f81329e97 100644 --- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs @@ -26,7 +26,7 @@ public class GifFrameMetadata : IFormatFrameMetadata { this.ColorTableMode = other.ColorTableMode; this.FrameDelay = other.FrameDelay; - this.DisposalMethod = other.DisposalMethod; + this.DisposalMode = other.DisposalMode; if (other.LocalColorTable?.Length > 0) { @@ -73,7 +73,7 @@ public class GifFrameMetadata : IFormatFrameMetadata /// Primarily used in Gif animation, this field indicates the way in which the graphic is to /// be treated after being displayed. /// - public FrameDisposalMode DisposalMethod { get; set; } + public FrameDisposalMode DisposalMode { get; set; } /// public static GifFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) @@ -100,7 +100,7 @@ public class GifFrameMetadata : IFormatFrameMetadata LocalColorTable = metadata.ColorTable, ColorTableMode = metadata.ColorTableMode, FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10), - DisposalMethod = metadata.DisposalMode, + DisposalMode = metadata.DisposalMode, HasTransparency = hasTransparency, TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue, }; @@ -109,10 +109,9 @@ public class GifFrameMetadata : IFormatFrameMetadata /// public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() { - // throw new NotImplementedException(); // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or // has a local palette with 256 colors and is not transparent we should use 'Source'. - bool blendSource = this.DisposalMethod == FrameDisposalMode.RestoreToBackground || (this.LocalColorTable?.Length == 256 && !this.HasTransparency); + bool blendSource = this.DisposalMode == FrameDisposalMode.RestoreToBackground || (this.LocalColorTable?.Length == 256 && !this.HasTransparency); // If the color table is global and frame has no transparency. Consider it 'Source' also. blendSource |= this.ColorTableMode == FrameColorTableMode.Global && !this.HasTransparency; @@ -122,7 +121,7 @@ public class GifFrameMetadata : IFormatFrameMetadata ColorTable = this.LocalColorTable, ColorTableMode = this.ColorTableMode, Duration = TimeSpan.FromMilliseconds(this.FrameDelay * 10), - DisposalMode = this.DisposalMethod, + DisposalMode = this.DisposalMode, BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, }; } @@ -158,7 +157,7 @@ public class GifFrameMetadata : IFormatFrameMetadata LocalColorTable = metadata.ColorTable, ColorTableMode = metadata.ColorTableMode, FrameDelay = (int)Math.Round(metadata.Duration.TotalMilliseconds / 10), - DisposalMethod = metadata.DisposalMode, + DisposalMode = metadata.DisposalMode, HasTransparency = hasTransparency, TransparencyIndex = hasTransparency ? unchecked((byte)index) : byte.MinValue, }; diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 4ebe046ba..9a234aa9b 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -129,16 +129,20 @@ public class GifMetadata : IFormatMetadata ? this.GlobalColorTable.Value.Span[this.BackgroundColorIndex] : Color.Transparent; + int bpp = this.GlobalColorTable.HasValue + ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.GlobalColorTable.Value.Length), 1, 8) + : 8; + return new() { AnimateRootFrame = true, BackgroundColor = color, ColorTable = this.GlobalColorTable, ColorTableMode = this.ColorTableMode, - PixelTypeInfo = new PixelTypeInfo(24) + PixelTypeInfo = new PixelTypeInfo(bpp) { ColorType = PixelColorType.Indexed, - ComponentInfo = PixelComponentInfo.Create(3, 24, 8, 8, 8), + ComponentInfo = PixelComponentInfo.Create(1, bpp, bpp), }, RepeatCount = this.RepeatCount, }; diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index 5fd2d5c1e..0f2d281f1 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -80,7 +80,7 @@ public static partial class MetadataExtensions { // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or // has a local palette with 256 colors and is not transparent we should use 'Source'. - bool blendSource = source.DisposalMethod == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); + bool blendSource = source.DisposalMode == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); // If the color table is global and frame has no transparency. Consider it 'Source' also. blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency; @@ -90,7 +90,7 @@ public static partial class MetadataExtensions ColorTable = source.LocalColorTable, ColorTableMode = source.ColorTableMode, Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), - DisposalMode = source.DisposalMethod, + DisposalMode = source.DisposalMode, BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, }; } diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs index b98321977..5c579eb4f 100644 --- a/src/ImageSharp/Formats/IImageFormat.cs +++ b/src/ImageSharp/Formats/IImageFormat.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats; @@ -14,12 +14,12 @@ public interface IImageFormat string Name { get; } /// - /// Gets the default mimetype that the image format uses + /// Gets the default mime type that the image format uses /// string DefaultMimeType { get; } /// - /// Gets all the mimetypes that have been used by this image format. + /// Gets all the mime types that have been used by this image format. /// IEnumerable MimeTypes { get; } diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 938e11db1..4e6b6f265 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Pbm; /// /// Provides PBM specific metadata information for the image. /// -public class PbmMetadata : IDeepCloneable +public class PbmMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -38,8 +40,97 @@ public class PbmMetadata : IDeepCloneable /// /// Gets or sets the data type of the pixel components. /// - public PbmComponentType ComponentType { get; set; } + public PbmComponentType ComponentType { get; set; } = PbmComponentType.Byte; + + /// + public static PbmMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + PbmColorType color; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.Luminance; + + switch (colorType) + { + case PixelColorType.Binary: + color = PbmColorType.BlackAndWhite; + break; + case PixelColorType.Luminance: + color = PbmColorType.Grayscale; + break; + default: + if (colorType.HasFlag(PixelColorType.RGB) || colorType.HasFlag(PixelColorType.BGR)) + { + color = PbmColorType.Rgb; + } + else + { + color = PbmColorType.Grayscale; + } + + break; + } + + PbmComponentType componentType = PbmComponentType.Short; + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + if (bpp == 1) + { + componentType = PbmComponentType.Bit; + } + else if (bpp <= 8) + { + componentType = PbmComponentType.Byte; + } + + return new PbmMetadata + { + ColorType = color, + ComponentType = componentType + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + int bpp; + PixelColorType colorType; + PixelComponentInfo info; + switch (this.ColorType) + { + case PbmColorType.BlackAndWhite: + bpp = 1; + colorType = PixelColorType.Binary; + info = PixelComponentInfo.Create(1, bpp, 1); + break; + case PbmColorType.Grayscale: + bpp = 8; + colorType = PixelColorType.Luminance; + info = PixelComponentInfo.Create(1, bpp, 8); + break; + case PbmColorType.Rgb: + bpp = 24; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + default: + bpp = 8; + colorType = PixelColorType.Luminance; + info = PixelComponentInfo.Create(1, bpp, 8); + break; + } + + return new FormatConnectingMetadata + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, + }, + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); /// - public IDeepCloneable DeepClone() => new PbmMetadata(this); + public PbmMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs index fb2ca473c..91f79c815 100644 --- a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs +++ b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs @@ -22,8 +22,8 @@ internal readonly struct FrameControl uint yOffset, ushort delayNumerator, ushort delayDenominator, - PngDisposalMethod disposeOperation, - PngBlendMethod blendOperation) + FrameDisposalMode disposalMode, + FrameBlendMode blendMode) { this.SequenceNumber = sequenceNumber; this.Width = width; @@ -32,8 +32,8 @@ internal readonly struct FrameControl this.YOffset = yOffset; this.DelayNumerator = delayNumerator; this.DelayDenominator = delayDenominator; - this.DisposeOperation = disposeOperation; - this.BlendOperation = blendOperation; + this.DisposalMode = disposalMode; + this.BlendMode = blendMode; } /// @@ -84,12 +84,12 @@ internal readonly struct FrameControl /// /// Gets the type of frame area disposal to be done after rendering this frame /// - public PngDisposalMethod DisposeOperation { get; } + public FrameDisposalMode DisposalMode { get; } /// /// Gets the type of frame area rendering for this frame /// - public PngBlendMethod BlendOperation { get; } + public FrameBlendMode BlendMode { get; } public Rectangle Bounds => new((int)this.XOffset, (int)this.YOffset, (int)this.Width, (int)this.Height); @@ -137,8 +137,8 @@ internal readonly struct FrameControl BinaryPrimitives.WriteUInt16BigEndian(buffer[20..22], this.DelayNumerator); BinaryPrimitives.WriteUInt16BigEndian(buffer[22..24], this.DelayDenominator); - buffer[24] = (byte)this.DisposeOperation; - buffer[25] = (byte)this.BlendOperation; + buffer[24] = (byte)(this.DisposalMode - 1); + buffer[25] = (byte)this.BlendMode; } /// @@ -155,6 +155,6 @@ internal readonly struct FrameControl yOffset: BinaryPrimitives.ReadUInt32BigEndian(data[16..20]), delayNumerator: BinaryPrimitives.ReadUInt16BigEndian(data[20..22]), delayDenominator: BinaryPrimitives.ReadUInt16BigEndian(data[22..24]), - disposeOperation: (PngDisposalMethod)data[24], - blendOperation: (PngBlendMethod)data[25]); + disposalMode: (FrameDisposalMode)(data[24] + 1), + blendMode: (FrameBlendMode)data[25]); } diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs index b6313bffe..6da1fb5fd 100644 --- a/src/ImageSharp/Formats/Png/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Png/MetadataExtensions.cs @@ -69,16 +69,16 @@ public static partial class MetadataExtensions { ColorTableMode = FrameColorTableMode.Global, Duration = TimeSpan.FromMilliseconds(delay * 1000), - DisposalMode = GetMode(source.DisposalMethod), - BlendMode = source.BlendMethod == PngBlendMethod.Source ? FrameBlendMode.Source : FrameBlendMode.Over, + DisposalMode = GetMode(source.DisposalMode), + BlendMode = source.BlendMode, }; } - private static FrameDisposalMode GetMode(PngDisposalMethod method) => method switch + private static FrameDisposalMode GetMode(FrameDisposalMode method) => method switch { - PngDisposalMethod.DoNotDispose => FrameDisposalMode.DoNotDispose, - PngDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - PngDisposalMethod.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - _ => FrameDisposalMode.Unspecified, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, + _ => FrameDisposalMode.DoNotDispose, }; } diff --git a/src/ImageSharp/Formats/Png/PngBlendMethod.cs b/src/ImageSharp/Formats/Png/PngBlendMethod.cs deleted file mode 100644 index f71dce832..000000000 --- a/src/ImageSharp/Formats/Png/PngBlendMethod.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png; - -/// -/// Specifies whether the frame is to be alpha blended into the current output buffer content, -/// or whether it should completely replace its region in the output buffer. -/// -public enum PngBlendMethod -{ - /// - /// All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. - /// - Source, - - /// - /// The frame should be composited onto the output buffer based on its alpha, using a simple OVER operation as - /// described in the "Alpha Channel Processing" section of the PNG specification [PNG-1.2]. - /// - Over -} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 36a0a8bcb..a4d13177d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -246,7 +246,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals cancellationToken); // if current frame dispose is restore to previous, then from future frame's perspective, it never happened - if (currentFrameControl.Value.DisposeOperation != PngDisposalMethod.RestoreToPrevious) + if (currentFrameControl.Value.DisposalMode != FrameDisposalMode.RestoreToPrevious) { previousFrame = currentFrame; previousFrameControl = currentFrameControl; @@ -659,12 +659,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // If the first `fcTL` chunk uses a `dispose_op` of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND. // So, if restoring to before first frame, clear entire area. Same if first frame (previousFrameControl null). - if (previousFrameControl == null || (previousFrame is null && previousFrameControl.Value.DisposeOperation == PngDisposalMethod.RestoreToPrevious)) + if (previousFrameControl == null || (previousFrame is null && previousFrameControl.Value.DisposalMode == FrameDisposalMode.RestoreToPrevious)) { Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(); pixelRegion.Clear(); } - else if (previousFrameControl.Value.DisposeOperation == PngDisposalMethod.RestoreToBackground) + else if (previousFrameControl.Value.DisposalMode == FrameDisposalMode.RestoreToBackground) { Rectangle restoreArea = previousFrameControl.Value.Bounds; Buffer2DRegion pixelRegion = frame.PixelBuffer.GetRegion(restoreArea); @@ -794,8 +794,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals int height = (int)frameControl.YMax; IMemoryOwner? blendMemory = null; - Span blendRowBuffer = Span.Empty; - if (frameControl.BlendOperation == PngBlendMethod.Over) + Span blendRowBuffer = []; + if (frameControl.BlendMode == FrameBlendMode.Over) { blendMemory = this.memoryAllocator.Allocate(imageFrame.Width, AllocationOptions.Clean); blendRowBuffer = blendMemory.Memory.Span; @@ -887,8 +887,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals Buffer2D imageBuffer = imageFrame.PixelBuffer; IMemoryOwner? blendMemory = null; - Span blendRowBuffer = Span.Empty; - if (frameControl.BlendOperation == PngBlendMethod.Over) + Span blendRowBuffer = []; + if (frameControl.BlendMode == FrameBlendMode.Over) { blendMemory = this.memoryAllocator.Allocate(imageFrame.Width, AllocationOptions.Clean); blendRowBuffer = blendMemory.Memory.Span; @@ -1013,7 +1013,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { Span destination = pixels.PixelBuffer.DangerousGetRowSpan(currentRow); - bool blend = frameControl.BlendOperation == PngBlendMethod.Over; + bool blend = frameControl.BlendMode == FrameBlendMode.Over; Span rowSpan = blend ? blendRowBuffer : destination; @@ -1126,7 +1126,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals int increment = 1) where TPixel : unmanaged, IPixel { - bool blend = frameControl.BlendOperation == PngBlendMethod.Over; + bool blend = frameControl.BlendMode == FrameBlendMode.Over; Span rowSpan = blend ? blendRowBuffer : destination; @@ -1460,7 +1460,20 @@ internal sealed class PngDecoderCore : IImageDecoderInternals byte colorPrimaries = data[0]; byte transferFunction = data[1]; byte matrixCoefficients = data[2]; - bool? fullRange = data[3] == 1 ? true : data[3] == 0 ? false : null; + bool? fullRange; + if (data[3] == 1) + { + fullRange = true; + } + else if (data[3] == 0) + { + fullRange = false; + } + else + { + fullRange = null; + } + metadata.CicpProfile = new CicpProfile(colorPrimaries, transferFunction, matrixCoefficients, fullRange); } @@ -1492,7 +1505,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // Sequence of bytes for the exif header ("Exif" ASCII and two zero bytes). // This doesn't actually allocate. - ReadOnlySpan exifHeader = new byte[] { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; + ReadOnlySpan exifHeader = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; if (dataLength < exifHeader.Length) { @@ -1603,7 +1616,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals Span destUncompressedData = destBuffer.GetSpan(); if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) { - uncompressedBytesArray = Array.Empty(); + uncompressedBytesArray = []; return false; } @@ -1612,7 +1625,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { if (memoryStreamOutput.Length > maxLength) { - uncompressedBytesArray = Array.Empty(); + uncompressedBytesArray = []; return false; } @@ -1979,7 +1992,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { if (length == 0) { - return new BasicArrayBuffer(Array.Empty()); + return new BasicArrayBuffer([]); } // We rent the buffer here to return it afterwards in Decode() diff --git a/src/ImageSharp/Formats/Png/PngDisposalMethod.cs b/src/ImageSharp/Formats/Png/PngDisposalMethod.cs deleted file mode 100644 index 1537c5ced..000000000 --- a/src/ImageSharp/Formats/Png/PngDisposalMethod.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png; - -/// -/// Specifies how the output buffer should be changed at the end of the delay (before rendering the next frame). -/// -public enum PngDisposalMethod -{ - /// - /// No disposal is done on this frame before rendering the next; the contents of the output buffer are left as is. - /// - DoNotDispose, - - /// - /// The frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame. - /// - RestoreToBackground, - - /// - /// The frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame. - /// - RestoreToPrevious -} diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 36ce136f4..49157272c 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -212,7 +212,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Write the first animated frame. currentFrame = image.Frames[currentFrameIndex]; PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame); - PngDisposalMethod previousDisposal = frameMetadata.DisposalMethod; + FrameDisposalMode previousDisposal = frameMetadata.DisposalMode; FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); uint sequenceNumber = 1; if (pngMetadata.AnimateRootFrame) @@ -237,12 +237,12 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++) { - ImageFrame? prev = previousDisposal == PngDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame; currentFrame = image.Frames[currentFrameIndex]; ImageFrame? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null; frameMetadata = GetPngFrameMetadata(currentFrame); - bool blend = frameMetadata.BlendMethod == PngBlendMethod.Over; + bool blend = frameMetadata.BlendMode == FrameBlendMode.Over; (bool difference, Rectangle bounds) = AnimationUtilities.DeDuplicatePixels( @@ -268,7 +268,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable sequenceNumber += this.WriteDataChunks(frameControl, encodingFrame.PixelBuffer.GetRegion(bounds), quantized, stream, true) + 1; previousFrame = currentFrame; - previousDisposal = frameMetadata.DisposalMethod; + previousDisposal = frameMetadata.DisposalMode; } } @@ -293,7 +293,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) { - return (PngMetadata)png.DeepClone(); + return png.DeepClone(); } if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) @@ -317,7 +317,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) { - return (PngFrameMetadata)png.DeepClone(); + return png.DeepClone(); } if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) @@ -881,6 +881,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// /// The containing image data. /// The image meta data. + /// CICP matrix coefficients other than Identity are not supported in PNG. private void WriteCicpChunk(Stream stream, ImageMetadata metaData) { if (metaData.CicpProfile is null) @@ -1125,8 +1126,8 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable yOffset: (uint)bounds.Top, delayNumerator: (ushort)frameMetadata.FrameDelay.Numerator, delayDenominator: (ushort)frameMetadata.FrameDelay.Denominator, - disposeOperation: frameMetadata.DisposalMethod, - blendOperation: frameMetadata.BlendMethod); + disposalMode: frameMetadata.DisposalMode, + blendMode: frameMetadata.BlendMode); fcTL.WriteTo(this.chunkDataBuffer.Span); diff --git a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs index dbda4d73c..de141909a 100644 --- a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png; /// /// Provides APng specific metadata information for the image frame. /// -public class PngFrameMetadata : IDeepCloneable +public class PngFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -24,8 +24,8 @@ public class PngFrameMetadata : IDeepCloneable private PngFrameMetadata(PngFrameMetadata other) { this.FrameDelay = other.FrameDelay; - this.DisposalMethod = other.DisposalMethod; - this.BlendMethod = other.BlendMethod; + this.DisposalMode = other.DisposalMode; + this.BlendMode = other.BlendMode; } /// @@ -39,12 +39,12 @@ public class PngFrameMetadata : IDeepCloneable /// /// Gets or sets the type of frame area disposal to be done after rendering this frame /// - public PngDisposalMethod DisposalMethod { get; set; } + public FrameDisposalMode DisposalMode { get; set; } /// /// Gets or sets the type of frame area rendering for this frame /// - public PngBlendMethod BlendMethod { get; set; } + public FrameBlendMode BlendMode { get; set; } /// /// Initializes a new instance of the class. @@ -53,26 +53,56 @@ public class PngFrameMetadata : IDeepCloneable internal void FromChunk(in FrameControl frameControl) { this.FrameDelay = new Rational(frameControl.DelayNumerator, frameControl.DelayDenominator); - this.DisposalMethod = frameControl.DisposeOperation; - this.BlendMethod = frameControl.BlendOperation; + this.DisposalMode = frameControl.DisposalMode; + this.BlendMode = frameControl.BlendMode; } - /// - public IDeepCloneable DeepClone() => new PngFrameMetadata(this); - internal static PngFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) => new() { FrameDelay = new(metadata.Duration.TotalMilliseconds / 1000), - DisposalMethod = GetMode(metadata.DisposalMode), - BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? PngBlendMethod.Source : PngBlendMethod.Over, + DisposalMode = GetMode(metadata.DisposalMode), + BlendMode = metadata.BlendMode, }; - private static PngDisposalMethod GetMode(FrameDisposalMode mode) => mode switch + private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch { - FrameDisposalMode.RestoreToBackground => PngDisposalMethod.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => PngDisposalMethod.RestoreToPrevious, - FrameDisposalMode.DoNotDispose => PngDisposalMethod.DoNotDispose, - _ => PngDisposalMethod.DoNotDispose, + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + _ => FrameDisposalMode.DoNotDispose, }; + + /// + public static PngFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + => new() + { + FrameDelay = new(metadata.Duration.TotalMilliseconds / 1000), + DisposalMode = GetMode(metadata.DisposalMode), + BlendMode = metadata.BlendMode, + }; + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + { + double delay = this.FrameDelay.ToDouble(); + if (double.IsNaN(delay)) + { + delay = 0; + } + + return new() + { + ColorTableMode = FrameColorTableMode.Global, + Duration = TimeSpan.FromMilliseconds(delay * 1000), + DisposalMode = this.DisposalMode, + BlendMode = this.BlendMode, + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public PngFrameMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index de02e390f..49799f54b 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -2,13 +2,14 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Png.Chunks; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Png; /// /// Provides Png specific metadata information for the image. /// -public class PngMetadata : IDeepCloneable +public class PngMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -89,9 +90,6 @@ public class PngMetadata : IDeepCloneable /// public bool AnimateRootFrame { get; set; } = true; - /// - public IDeepCloneable DeepClone() => new PngMetadata(this); - internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) { // Should the conversion be from a format that uses a 24bit palette entries (gif) @@ -121,4 +119,161 @@ public class PngMetadata : IDeepCloneable RepeatCount = metadata.RepeatCount, }; } + + /// + public static PngMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + // Should the conversion be from a format that uses a 24bit palette entries (gif) + // we need to clone and adjust the color table to allow for transparency. + Color[]? colorTable = metadata.ColorTable?.ToArray(); + if (colorTable != null) + { + for (int i = 0; i < colorTable.Length; i++) + { + ref Color c = ref colorTable[i]; + if (c == metadata.BackgroundColor) + { + // Png treats background as fully empty + c = Color.Transparent; + break; + } + } + } + + PngColorType color; + PixelColorType colorType = + metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + + switch (colorType) + { + case PixelColorType.Binary: + case PixelColorType.Indexed: + color = PngColorType.Palette; + break; + case PixelColorType.Luminance: + color = PngColorType.Grayscale; + break; + case PixelColorType.RGB: + case PixelColorType.BGR: + color = PngColorType.Rgb; + break; + default: + if (colorType.HasFlag(PixelColorType.Luminance)) + { + color = PngColorType.GrayscaleWithAlpha; + break; + } + + color = PngColorType.RgbWithAlpha; + break; + } + + // PNG uses bits per component not per pixel. + int bpc = metadata.PixelTypeInfo.ComponentInfo?.GetMaximumComponentPrecision() ?? 8; + PngBitDepth bitDepth = bpc switch + { + 1 => PngBitDepth.Bit1, + 2 => PngBitDepth.Bit2, + 4 => PngBitDepth.Bit4, + _ => (bpc <= 8) ? PngBitDepth.Bit8 : PngBitDepth.Bit16, + }; + return new() + { + ColorType = color, + BitDepth = bitDepth, + ColorTable = colorTable, + RepeatCount = metadata.RepeatCount, + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + int bpp; + PixelColorType colorType; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + PixelComponentInfo info; + switch (this.ColorType) + { + case PngColorType.Palette: + bpp = this.ColorTable.HasValue + ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.ColorTable.Value.Length), 1, 8) + : 8; + + colorType = PixelColorType.Indexed; + info = PixelComponentInfo.Create(1, bpp, bpp); + break; + + case PngColorType.Grayscale: + bpp = 8; + colorType = PixelColorType.Luminance; + info = PixelComponentInfo.Create(1, bpp, 8); + break; + + case PngColorType.GrayscaleWithAlpha: + + alpha = PixelAlphaRepresentation.Unassociated; + if (this.BitDepth == PngBitDepth.Bit16) + { + bpp = 32; + colorType = PixelColorType.Luminance | PixelColorType.Alpha; + info = PixelComponentInfo.Create(2, bpp, 16, 16); + break; + } + + bpp = 16; + colorType = PixelColorType.Luminance | PixelColorType.Alpha; + info = PixelComponentInfo.Create(2, bpp, 8, 8); + break; + + case PngColorType.Rgb: + if (this.BitDepth == PngBitDepth.Bit16) + { + bpp = 24; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + } + + bpp = 48; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 16, 16, 16); + break; + + default: + + alpha = PixelAlphaRepresentation.Unassociated; + if (this.BitDepth == PngBitDepth.Bit16) + { + bpp = 64; + colorType = PixelColorType.RGB | PixelColorType.Alpha; + info = PixelComponentInfo.Create(4, bpp, 16, 16, 16, 16); + break; + } + + bpp = 32; + colorType = PixelColorType.RGB | PixelColorType.Alpha; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + break; + } + + return new() + { + ColorTable = this.ColorTable, + ColorTableMode = FrameColorTableMode.Global, + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, + }, + RepeatCount = (ushort)Numerics.Clamp(this.RepeatCount, 0, ushort.MaxValue), + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public PngMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 6b62be08f..ceeea42cf 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -33,7 +33,7 @@ public sealed class ImageMetadata : IDeepCloneable /// public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; - private readonly Dictionary formatMetadata = new(); + private readonly Dictionary formatMetadata = []; private double horizontalResolution; private double verticalResolution; diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs index afbe6a4d3..315c0f658 100644 --- a/src/ImageSharp/PixelFormats/PixelColorType.cs +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -44,55 +44,60 @@ public enum PixelColorType /// Luminance = 1 << 5, + /// + /// Indicates that the color is in binary (black and white) format. + /// + Binary = 1 << 6, + /// /// Indicates that the color is indexed using a palette. /// - Indexed = 1 << 6, + Indexed = 1 << 7, /// /// Indicates that the color is in RGB (Red, Green, Blue) format. /// - RGB = Red | Green | Blue | (1 << 7), + RGB = Red | Green | Blue | (1 << 8), /// /// Indicates that the color is in BGR (Blue, Green, Red) format. /// - BGR = Blue | Green | Red | (1 << 8), + BGR = Blue | Green | Red | (1 << 9), /// - /// Represents the Chrominance Blue component in YCbCr. + /// Represents the Chrominance Blue component. /// - ChrominanceBlue = 1 << 9, + ChrominanceBlue = 1 << 10, /// - /// Represents the Chrominance Red component in YCbCr. + /// Represents the Chrominance Red component. /// - ChrominanceRed = 1 << 10, + ChrominanceRed = 1 << 11, /// /// Indicates that the color is in YCbCr (Luminance, Chrominance Blue, Chrominance Red) format. /// - YCbCr = Luminance | ChrominanceBlue | ChrominanceRed, + YCbCr = Luminance | ChrominanceBlue | ChrominanceRed | (1 << 12), /// /// Represents the Cyan component in CMYK. /// - Cyan = 1 << 11, + Cyan = 1 << 13, /// /// Represents the Magenta component in CMYK. /// - Magenta = 1 << 12, + Magenta = 1 << 14, /// /// Represents the Yellow component in CMYK. /// - Yellow = 1 << 13, + Yellow = 1 << 15, /// /// Represents the Key (black) component in CMYK and YCCK. /// - Key = 1 << 14, + Key = 1 << 16, /// /// Indicates that the color is in CMYK (Cyan, Magenta, Yellow, Key) format. @@ -107,5 +112,5 @@ public enum PixelColorType /// /// Indicates that the color is of a type not specified in this enum. /// - Other = 1 << 16 + Other = 1 << 17 } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 0b6615b8e..aeb3bab7b 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -306,14 +306,14 @@ public class GifEncoderTests Assert.Equal((int)(pngF.FrameDelay.ToDouble() * 100), gifF.FrameDelay); - switch (pngF.DisposalMethod) + switch (pngF.DisposalMode) { - case PngDisposalMethod.RestoreToBackground: - Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod); + case FrameDisposalMode.RestoreToBackground: + Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMode); break; - case PngDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMode); break; } } @@ -359,11 +359,11 @@ public class GifEncoderTests switch (webpF.DisposalMethod) { case WebpDisposalMethod.RestoreToBackground: - Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMode); break; case WebpDisposalMethod.DoNotDispose: default: - Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMode); break; } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs index f92896b7e..f12e6e7e5 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs @@ -15,18 +15,18 @@ public class GifFrameMetadataTests GifFrameMetadata meta = new() { FrameDelay = 1, - DisposalMethod = FrameDisposalMode.RestoreToBackground, + DisposalMode = FrameDisposalMode.RestoreToBackground, LocalColorTable = new[] { Color.Black, Color.White } }; GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone(); clone.FrameDelay = 2; - clone.DisposalMethod = FrameDisposalMode.RestoreToPrevious; + clone.DisposalMode = FrameDisposalMode.RestoreToPrevious; clone.LocalColorTable = new[] { Color.Black }; Assert.False(meta.FrameDelay.Equals(clone.FrameDelay)); - Assert.False(meta.DisposalMethod.Equals(clone.DisposalMethod)); + Assert.False(meta.DisposalMode.Equals(clone.DisposalMode)); Assert.False(meta.LocalColorTable.Value.Length == clone.LocalColorTable.Value.Length); Assert.Equal(1, clone.LocalColorTable.Value.Length); } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index afaa827bf..e61be8235 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -212,6 +212,6 @@ public class GifMetadataTests } Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); - Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); + Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMode); } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 1e4472e8a..e61475b65 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -479,8 +479,8 @@ public partial class PngEncoderTests PngFrameMetadata outputFrameMetadata = output.Frames[i].Metadata.GetPngMetadata(); Assert.Equal(originalFrameMetadata.FrameDelay, outputFrameMetadata.FrameDelay); - Assert.Equal(originalFrameMetadata.BlendMethod, outputFrameMetadata.BlendMethod); - Assert.Equal(originalFrameMetadata.DisposalMethod, outputFrameMetadata.DisposalMethod); + Assert.Equal(originalFrameMetadata.BlendMode, outputFrameMetadata.BlendMode); + Assert.Equal(originalFrameMetadata.DisposalMode, outputFrameMetadata.DisposalMode); } } @@ -519,18 +519,18 @@ public partial class PngEncoderTests Assert.Equal(gifF.FrameDelay, (int)(pngF.FrameDelay.ToDouble() * 100)); - switch (gifF.DisposalMethod) + switch (gifF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: - Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, pngF.DisposalMode); break; case FrameDisposalMode.RestoreToPrevious: - Assert.Equal(PngDisposalMethod.RestoreToPrevious, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToPrevious, pngF.DisposalMode); break; case FrameDisposalMode.Unspecified: case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, pngF.DisposalMode); break; } } @@ -570,22 +570,22 @@ public partial class PngEncoderTests switch (webpF.BlendMethod) { case WebpBlendMethod.Source: - Assert.Equal(PngBlendMethod.Source, pngF.BlendMethod); + Assert.Equal(FrameBlendMode.Source, pngF.BlendMode); break; case WebpBlendMethod.Over: default: - Assert.Equal(PngBlendMethod.Over, pngF.BlendMethod); + Assert.Equal(FrameBlendMode.Over, pngF.BlendMode); break; } switch (webpF.DisposalMethod) { case WebpDisposalMethod.RestoreToBackground: - Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, pngF.DisposalMode); break; case WebpDisposalMethod.DoNotDispose: default: - Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, pngF.DisposalMode); break; } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs index 9ba261728..8fde21654 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; namespace SixLabors.ImageSharp.Tests.Formats.Png; @@ -14,22 +15,22 @@ public class PngFrameMetadataTests PngFrameMetadata meta = new() { FrameDelay = new(1, 0), - DisposalMethod = PngDisposalMethod.RestoreToBackground, - BlendMethod = PngBlendMethod.Over, + DisposalMode = FrameDisposalMode.RestoreToBackground, + BlendMode = FrameBlendMode.Over, }; - PngFrameMetadata clone = (PngFrameMetadata)meta.DeepClone(); + PngFrameMetadata clone = meta.DeepClone(); Assert.True(meta.FrameDelay.Equals(clone.FrameDelay)); - Assert.True(meta.DisposalMethod.Equals(clone.DisposalMethod)); - Assert.True(meta.BlendMethod.Equals(clone.BlendMethod)); + Assert.True(meta.DisposalMode.Equals(clone.DisposalMode)); + Assert.True(meta.BlendMode.Equals(clone.BlendMode)); clone.FrameDelay = new(2, 1); - clone.DisposalMethod = PngDisposalMethod.RestoreToPrevious; - clone.BlendMethod = PngBlendMethod.Source; + clone.DisposalMode = FrameDisposalMode.RestoreToPrevious; + clone.BlendMode = FrameBlendMode.Source; Assert.False(meta.FrameDelay.Equals(clone.FrameDelay)); - Assert.False(meta.DisposalMethod.Equals(clone.DisposalMethod)); - Assert.False(meta.BlendMethod.Equals(clone.BlendMethod)); + Assert.False(meta.DisposalMode.Equals(clone.DisposalMode)); + Assert.False(meta.BlendMode.Equals(clone.BlendMode)); } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 49c47adfd..4d7236307 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -95,7 +95,7 @@ public class WebpEncoderTests Assert.Equal(gifF.FrameDelay, (int)(webpF.FrameDelay / 10)); - switch (gifF.DisposalMethod) + switch (gifF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); @@ -144,23 +144,23 @@ public class WebpEncoderTests Assert.Equal((uint)(pngF.FrameDelay.ToDouble() * 1000), webpF.FrameDelay); - switch (pngF.BlendMethod) + switch (pngF.BlendMode) { - case PngBlendMethod.Source: + case FrameBlendMode.Source: Assert.Equal(WebpBlendMethod.Source, webpF.BlendMethod); break; - case PngBlendMethod.Over: + case FrameBlendMode.Over: default: Assert.Equal(WebpBlendMethod.Over, webpF.BlendMethod); break; } - switch (pngF.DisposalMethod) + switch (pngF.DisposalMode) { - case PngDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); break; - case PngDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); break; diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index 572c5b2eb..f70623f51 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -313,7 +313,7 @@ public abstract partial class ImageFrameCollectionTests GifFrameMetadata aData = a.Metadata.GetGifMetadata(); GifFrameMetadata bData = b.Metadata.GetGifMetadata(); - Assert.Equal(aData.DisposalMethod, bData.DisposalMethod); + Assert.Equal(aData.DisposalMode, bData.DisposalMode); Assert.Equal(aData.FrameDelay, bData.FrameDelay); if (aData.ColorTableMode == FrameColorTableMode.Local && bData.ColorTableMode == FrameColorTableMode.Local) diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs index 9cb7137fe..cdd6f0cc4 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs @@ -28,14 +28,14 @@ public class ImageFrameMetadataTests GifFrameMetadata gifFrameMetadata = metaData.GetGifMetadata(); gifFrameMetadata.FrameDelay = frameDelay; gifFrameMetadata.LocalColorTable = Enumerable.Repeat(Color.HotPink, colorTableLength).ToArray(); - gifFrameMetadata.DisposalMethod = disposalMethod; + gifFrameMetadata.DisposalMode = disposalMethod; ImageFrameMetadata clone = new(metaData); GifFrameMetadata cloneGifFrameMetadata = clone.GetGifMetadata(); Assert.Equal(frameDelay, cloneGifFrameMetadata.FrameDelay); Assert.Equal(colorTableLength, cloneGifFrameMetadata.LocalColorTable.Value.Length); - Assert.Equal(disposalMethod, cloneGifFrameMetadata.DisposalMethod); + Assert.Equal(disposalMethod, cloneGifFrameMetadata.DisposalMode); } [Fact] diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs index c42713005..05fd25cca 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -49,6 +49,13 @@ public class PixelColorTypeTests Assert.True(colorType.HasFlag(PixelColorType.Luminance)); } + [Fact] + public void PixelColorType_Binary_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Binary; + Assert.True(colorType.HasFlag(PixelColorType.Binary)); + } + [Fact] public void PixelColorType_Indexed_ShouldBeSet() { @@ -168,6 +175,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); @@ -185,6 +193,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); @@ -204,6 +213,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); @@ -222,6 +232,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); @@ -237,6 +248,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Blue)); Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.Cyan)); Assert.False(colorType.HasFlag(PixelColorType.Magenta)); @@ -244,6 +256,31 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Other)); } + [Fact] + public void PixelColorType_Binary_ShouldNotContainOtherFlags() + { + const PixelColorType colorType = PixelColorType.Binary; + Assert.False(colorType.HasFlag(PixelColorType.Red)); + Assert.False(colorType.HasFlag(PixelColorType.Green)); + Assert.False(colorType.HasFlag(PixelColorType.Blue)); + Assert.False(colorType.HasFlag(PixelColorType.Alpha)); + Assert.False(colorType.HasFlag(PixelColorType.Exponent)); + Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Indexed)); + Assert.False(colorType.HasFlag(PixelColorType.RGB)); + Assert.False(colorType.HasFlag(PixelColorType.BGR)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); + Assert.False(colorType.HasFlag(PixelColorType.ChrominanceRed)); + Assert.False(colorType.HasFlag(PixelColorType.YCbCr)); + Assert.False(colorType.HasFlag(PixelColorType.Cyan)); + Assert.False(colorType.HasFlag(PixelColorType.Magenta)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + Assert.False(colorType.HasFlag(PixelColorType.Key)); + Assert.False(colorType.HasFlag(PixelColorType.CMYK)); + Assert.False(colorType.HasFlag(PixelColorType.YCCK)); + Assert.False(colorType.HasFlag(PixelColorType.Other)); + } + [Fact] public void PixelColorType_Indexed_ShouldNotContainOtherFlags() { @@ -254,6 +291,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.RGB)); Assert.False(colorType.HasFlag(PixelColorType.BGR)); Assert.False(colorType.HasFlag(PixelColorType.ChrominanceBlue)); @@ -278,6 +316,7 @@ public class PixelColorTypeTests Assert.False(colorType.HasFlag(PixelColorType.Alpha)); Assert.False(colorType.HasFlag(PixelColorType.Exponent)); Assert.False(colorType.HasFlag(PixelColorType.Luminance)); + Assert.False(colorType.HasFlag(PixelColorType.Binary)); Assert.False(colorType.HasFlag(PixelColorType.Indexed)); Assert.False(colorType.HasFlag(PixelColorType.RGB)); Assert.False(colorType.HasFlag(PixelColorType.BGR)); From b9994b60db40d6fff53fcf116883baa15c1c742b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 28 May 2024 21:42:45 +1000 Subject: [PATCH 176/220] Implement QoiMetadata --- src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs | 8 +-- src/ImageSharp/Formats/Qoi/QoiMetadata.cs | 56 +++++++++++++++++++- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index 86d81d834..92974aade 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -49,13 +49,7 @@ internal class QoiDecoderCore : IImageDecoderInternals this.ProcessHeader(stream); // Create Image object - ImageMetadata metadata = new() - { - DecodedImageFormat = QoiFormat.Instance, - HorizontalResolution = this.header.Width, - VerticalResolution = this.header.Height, - ResolutionUnits = PixelResolutionUnit.AspectRatio - }; + ImageMetadata metadata = new(); QoiMetadata qoiMetadata = metadata.GetQoiMetadata(); qoiMetadata.Channels = this.header.Channels; qoiMetadata.ColorSpace = this.header.ColorSpace; diff --git a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs index 610c6c15b..4be714fc3 100644 --- a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs +++ b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Qoi; /// /// Provides Qoi specific metadata information for the image. /// -public class QoiMetadata : IDeepCloneable +public class QoiMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -36,5 +38,55 @@ public class QoiMetadata : IDeepCloneable public QoiColorSpace ColorSpace { get; set; } /// - public IDeepCloneable DeepClone() => new QoiMetadata(this); + public static QoiMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + PixelColorType color = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB; + + if (color.HasFlag(PixelColorType.Alpha)) + { + return new QoiMetadata { Channels = QoiChannels.Rgba }; + } + + return new QoiMetadata { Channels = QoiChannels.Rgb }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + int bpp; + PixelColorType colorType; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + PixelComponentInfo info; + + switch (this.Channels) + { + case QoiChannels.Rgb: + bpp = 24; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + default: + bpp = 32; + colorType = PixelColorType.RGB | PixelColorType.Alpha; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + + return new() + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, + } + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public QoiMetadata DeepClone() => new(this); } From 1f4605ed3b8b78bed5e24ac43c205c6897e331cd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 May 2024 19:58:35 +1000 Subject: [PATCH 177/220] Implement TgaMetadata --- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 42 +++++------- src/ImageSharp/Formats/Tga/TgaMetadata.cs | 66 ++++++++++++++++++- .../Formats/Tga/TgaEncoderTests.cs | 1 + 3 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index bb13798c5..14351ee67 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -3,8 +3,6 @@ using System.Buffers; using System.Buffers.Binary; -using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -160,19 +158,26 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals where TPixel : unmanaged, IPixel { Buffer2D pixels = image.PixelBuffer; + + using IMemoryOwner rgbaOwner = this.memoryAllocator.Allocate(image.Width); + Span rgbaRow = rgbaOwner.GetSpan(); + for (int y = 0; y < image.Height; y++) { Span pixelRow = pixels.DangerousGetRowSpan(y); + PixelOperations.Instance.ToRgba32(image.Configuration, pixelRow, rgbaRow); + for (int x = 0; x < image.Width;) { TPixel currentPixel = pixelRow[x]; + Rgba32 rgba = rgbaRow[x]; byte equalPixelCount = FindEqualPixels(pixelRow, x); if (equalPixelCount > 0) { - // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. + // Write the number of equal pixels, with the high bit set, indicating it's a compressed pixel run. stream.WriteByte((byte)(equalPixelCount | 128)); - this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); + this.WritePixel(stream, rgba); x += equalPixelCount + 1; } else @@ -180,12 +185,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals // Write Raw Packet (i.e., Non-Run-Length Encoded): byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x); stream.WriteByte(unEqualPixelCount); - this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); + this.WritePixel(stream, rgba); x++; for (int i = 0; i < unEqualPixelCount; i++) { currentPixel = pixelRow[x]; - this.WritePixel(stream, currentPixel, currentPixel.ToRgba32()); + rgba = rgbaRow[x]; + this.WritePixel(stream, rgba); x++; } } @@ -196,22 +202,19 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// /// Writes a the pixel to the stream. /// - /// The type of the pixel. /// The stream to write to. - /// The current pixel. /// The color of the pixel to write. - private void WritePixel(Stream stream, TPixel currentPixel, Rgba32 color) - where TPixel : unmanaged, IPixel + private void WritePixel(Stream stream, Rgba32 color) { switch (this.bitsPerPixel) { case TgaBitsPerPixel.Pixel8: - int luminance = GetLuminance(currentPixel); - stream.WriteByte((byte)luminance); + L8 l8 = L8.FromRgba32(color); + stream.WriteByte(l8.PackedValue); break; case TgaBitsPerPixel.Pixel16: - Bgra5551 bgra5551 = new(color.ToVector4()); + Bgra5551 bgra5551 = Bgra5551.FromRgba32(color); Span buffer = stackalloc byte[2]; BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)bgra5551.PackedValue); stream.WriteByte(buffer[0]); @@ -402,17 +405,4 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals stream.Write(rowSpan); } } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The type of pixel format. - /// The pixel to get the luminance from. - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(TPixel sourcePixel) - where TPixel : unmanaged, IPixel - { - Vector4 vector = sourcePixel.ToVector4(); - return ColorNumerics.GetBT709Luminance(ref vector, 256); - } } diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs index 1fb3ab5c5..345d1698e 100644 --- a/src/ImageSharp/Formats/Tga/TgaMetadata.cs +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Tga; /// /// Provides TGA specific metadata information for the image. /// -public class TgaMetadata : IDeepCloneable +public class TgaMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -33,5 +35,65 @@ public class TgaMetadata : IDeepCloneable public byte AlphaChannelBits { get; set; } /// - public IDeepCloneable DeepClone() => new TgaMetadata(this); + public static TgaMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + // TODO: AlphaChannelBits is not used during encoding. + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + return bpp switch + { + <= 8 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel8 }, + <= 16 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel16 }, + <= 24 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel24 }, + _ => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel32 } + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + { + int bpp = (int)this.BitsPerPixel; + + PixelComponentInfo info; + PixelColorType color; + PixelAlphaRepresentation alpha; + switch (this.BitsPerPixel) + { + case TgaBitsPerPixel.Pixel8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Luminance; + alpha = PixelAlphaRepresentation.None; + break; + case TgaBitsPerPixel.Pixel16: + info = PixelComponentInfo.Create(1, bpp, 5, 5, 5, 1); + color = PixelColorType.BGR | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + case TgaBitsPerPixel.Pixel24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + alpha = PixelAlphaRepresentation.None; + break; + case TgaBitsPerPixel.Pixel32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + + return new() + { + PixelTypeInfo = new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + } + }; + } + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public TgaMetadata DeepClone() => new(this); } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 709a3207a..a3fa082c6 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -175,6 +175,7 @@ public class TgaEncoderTests using (var memStream = new MemoryStream()) { + image.DebugSave(provider, encoder); image.Save(memStream, encoder); memStream.Position = 0; using (var encodedImage = (Image)Image.Load(memStream)) From be0be6933fa05ffc45b67de7e7ea205a0a80bb4c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 May 2024 19:58:51 +1000 Subject: [PATCH 178/220] Fix PngMetadata --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 77 ++----------------- src/ImageSharp/Formats/Png/PngMetadata.cs | 31 ++++---- .../Formats/Png/PngEncoderTests.cs | 43 ----------- .../Formats/Png/PngSmokeTests.cs | 12 +-- 4 files changed, 28 insertions(+), 135 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 49157272c..d1528d08b 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1484,41 +1484,19 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Use options, then check metadata, if nothing set there then we suggest // a sensible default based upon the pixel format. - PngColorType? colorType = encoder.ColorType ?? pngMetadata.ColorType; - byte? bits = (byte?)(encoder.BitDepth ?? pngMetadata.BitDepth); + PngColorType color = encoder.ColorType ?? pngMetadata.ColorType; + byte bits = (byte)(encoder.BitDepth ?? pngMetadata.BitDepth); - if (colorType is null || bits is null) - { - PixelTypeInfo info = TPixel.GetPixelTypeInfo(); - PixelComponentInfo? componentInfo = info.ComponentInfo; - - colorType ??= SuggestColorType(in info); - - if (bits is null) - { - // TODO: Update once we stop abusing PixelTypeInfo in decoders. - if (componentInfo.HasValue) - { - PixelComponentInfo c = componentInfo.Value; - bits = (byte)SuggestBitDepth(in c); - } - else - { - bits = (byte)PngBitDepth.Bit8; - } - } - } - - // Ensure bit depth and color type are a supported combination. + // Ensure the bit depth and color type are a supported combination. // Bit8 is the only bit depth supported by all color types. - byte[] validBitDepths = PngConstants.ColorTypes[colorType.Value]; + byte[] validBitDepths = PngConstants.ColorTypes[color]; if (Array.IndexOf(validBitDepths, bits) == -1) { bits = (byte)PngBitDepth.Bit8; } - this.colorType = colorType.Value; - this.bitDepth = bits.Value; + this.colorType = color; + this.bitDepth = bits; if (!encoder.FilterMethod.HasValue) { @@ -1529,7 +1507,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable use16Bit = bits == (byte)PngBitDepth.Bit16; bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit); - this.interlaceMode = (encoder.InterlaceMethod ?? pngMetadata.InterlaceMethod)!.Value; + this.interlaceMode = encoder.InterlaceMethod ?? pngMetadata.InterlaceMethod; this.chunkFilter = encoder.SkipMetadata ? PngChunkFilter.ExcludeAll : encoder.ChunkFilter ?? PngChunkFilter.None; } @@ -1669,47 +1647,6 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable _ => use16Bit ? 8 : 4, }; - /// - /// Returns a suggested for the given - /// - /// The pixel type info. - /// The type of pixel format. - private static PngColorType SuggestColorType(in PixelTypeInfo info) - where TPixel : unmanaged, IPixel - { - if (info.AlphaRepresentation == PixelAlphaRepresentation.None) - { - return info.ColorType switch - { - PixelColorType.Luminance => PngColorType.Grayscale, - _ => PngColorType.Rgb, - }; - } - - return info.ColorType switch - { - PixelColorType.Luminance | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha, - _ => PngColorType.RgbWithAlpha, - }; - } - - /// - /// Returns a suggested for the given - /// - /// The pixel type info. - /// The type of pixel format. - private static PngBitDepth SuggestBitDepth(in PixelComponentInfo info) - where TPixel : unmanaged, IPixel - { - int bits = info.GetMaximumComponentPrecision(); - if (bits > (int)PixelComponentBitDepth.Bit8) - { - return PngBitDepth.Bit16; - } - - return PngBitDepth.Bit8; - } - private unsafe struct ScratchBuffer { private const int Size = 26; diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index 49799f54b..d789ae8fa 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -47,17 +47,17 @@ public class PngMetadata : IFormatMetadata /// Gets or sets the number of bits per sample or per palette index (not per pixel). /// Not all values are allowed for all values. /// - public PngBitDepth? BitDepth { get; set; } + public PngBitDepth BitDepth { get; set; } = PngBitDepth.Bit8; /// /// Gets or sets the color type. /// - public PngColorType? ColorType { get; set; } + public PngColorType ColorType { get; set; } = PngColorType.RgbWithAlpha; /// /// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image. /// - public PngInterlaceMode? InterlaceMethod { get; set; } = PngInterlaceMode.None; + public PngInterlaceMode InterlaceMethod { get; set; } = PngInterlaceMode.None; /// /// Gets or sets the gamma value for the image. @@ -100,21 +100,23 @@ public class PngMetadata : IFormatMetadata for (int i = 0; i < colorTable.Length; i++) { ref Color c = ref colorTable[i]; - if (c == metadata.BackgroundColor) + if (c != metadata.BackgroundColor) { - // Png treats background as fully empty - c = Color.Transparent; - break; + continue; } + + // Png treats background as fully empty + c = Color.Transparent; + break; } } return new() { - ColorType = colorTable != null ? PngColorType.Palette : null, + ColorType = colorTable != null ? PngColorType.Palette : PngColorType.RgbWithAlpha, BitDepth = colorTable != null ? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8) - : null, + : PngBitDepth.Bit8, ColorTable = colorTable, RepeatCount = metadata.RepeatCount, }; @@ -131,12 +133,14 @@ public class PngMetadata : IFormatMetadata for (int i = 0; i < colorTable.Length; i++) { ref Color c = ref colorTable[i]; - if (c == metadata.BackgroundColor) + if (c != metadata.BackgroundColor) { - // Png treats background as fully empty - c = Color.Transparent; - break; + continue; } + + // Png treats background as fully empty + c = Color.Transparent; + break; } } @@ -240,6 +244,7 @@ public class PngMetadata : IFormatMetadata info = PixelComponentInfo.Create(3, bpp, 16, 16, 16); break; + case PngColorType.RgbWithAlpha: default: alpha = PixelAlphaRepresentation.Unassociated; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index e61475b65..f40e537a1 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -254,49 +254,6 @@ public partial class PngEncoderTests } } - [Theory] - [WithBlankImages(1, 1, PixelTypes.A8, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Argb32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.Rgb, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgra4444, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Byte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.Rgb, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.Rgb, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.Rgb, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedByte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.Rgb, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.RgbaVector, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.Rgb, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Rgb24, PngColorType.Rgb, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgr24, PngColorType.Rgb, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Bgra32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.Rgb48, PngColorType.Rgb, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Rgba64, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.Bgra5551, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.L8, PngColorType.Grayscale, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.L16, PngColorType.Grayscale, PngBitDepth.Bit16)] - [WithBlankImages(1, 1, PixelTypes.La16, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] - [WithBlankImages(1, 1, PixelTypes.La32, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit16)] - public void InfersColorTypeAndBitDepth(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) - where TPixel : unmanaged, IPixel - { - using Stream stream = new MemoryStream(); - PngEncoder.Encode(provider.GetImage(), stream); - - stream.Seek(0, SeekOrigin.Begin); - - using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream); - - PngMetadata metadata = image.Metadata.GetPngMetadata(); - Assert.Equal(pngColorType, metadata.ColorType); - Assert.Equal(pngBitDepth, metadata.BitDepth); - } - [Theory] [WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)] public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index a4288a3d8..3ca7730eb 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -17,17 +17,13 @@ public class PngSmokeTests public void GeneralTest(TestImageProvider provider) where TPixel : unmanaged, IPixel { - // does saving a file then reopening mean both files are identical??? using Image image = provider.GetImage(); - using var ms = new MemoryStream(); + using MemoryStream ms = new(); - // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.Save(ms, new PngEncoder()); ms.Position = 0; using Image img2 = PngDecoder.Instance.Decode(DecoderOptions.Default, ms); - ImageComparer.Tolerant().VerifySimilarity(image, img2); - - // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); + ImageComparer.Exact.VerifySimilarity(image, img2); } [Theory] @@ -37,12 +33,10 @@ public class PngSmokeTests { // does saving a file then reopening mean both files are identical??? using Image image = provider.GetImage(); - using var ms = new MemoryStream(); + using MemoryStream ms = new(); - // image.Save(provider.Utility.GetTestOutputFileName("png")); image.Mutate(x => x.Resize(100, 100)); - // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); image.Save(ms, new PngEncoder()); ms.Position = 0; using Image img2 = PngDecoder.Instance.Decode(DecoderOptions.Default, ms); From 4f6bd17120dcf60e7b66daa9bf3466a24d5ee499 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 May 2024 19:59:14 +1000 Subject: [PATCH 179/220] Fix PngMetadata --- src/ImageSharp/Formats/Png/PngDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 4a7ba3f96..e62a14b5e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -53,8 +53,8 @@ public sealed class PngDecoder : SpecializedImageDecoder stream.Position = 0; PngMetadata meta = info.Metadata.GetPngMetadata(); - PngColorType color = meta.ColorType.GetValueOrDefault(); - PngBitDepth bits = meta.BitDepth.GetValueOrDefault(); + PngColorType color = meta.ColorType; + PngBitDepth bits = meta.BitDepth; switch (color) { From 7103e28d9002071c1fb46007192c3725bbcd1564 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 May 2024 19:59:21 +1000 Subject: [PATCH 180/220] Cleanup --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 100 +++++++++++----------- src/ImageSharp/Formats/Gif/GifMetadata.cs | 16 ++-- src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 15 ++-- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index 00c5910d4..bb6b87e58 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -42,37 +42,25 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel1 }, + 2 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel2 }, + <= 4 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel4 }, + <= 8 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }, + <= 16 => new BmpMetadata + { + BitsPerPixel = BmpBitsPerPixel.Pixel16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 + }, + <= 24 => new BmpMetadata + { + BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 + }, + _ => new BmpMetadata + { + BitsPerPixel = BmpBitsPerPixel.Pixel32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 + } + }; } /// @@ -97,30 +85,42 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata bpp < 32 ? PixelAlphaRepresentation.None : PixelAlphaRepresentation.Unassociated }; - PixelComponentInfo info = this.BitsPerPixel switch + PixelComponentInfo info; + PixelColorType color; + switch (this.BitsPerPixel) { - BmpBitsPerPixel.Pixel1 => PixelComponentInfo.Create(1, bpp, 1), - BmpBitsPerPixel.Pixel2 => PixelComponentInfo.Create(1, bpp, 2), - BmpBitsPerPixel.Pixel4 => PixelComponentInfo.Create(1, bpp, 4), - BmpBitsPerPixel.Pixel8 => PixelComponentInfo.Create(1, bpp, 8), + case BmpBitsPerPixel.Pixel1: + info = PixelComponentInfo.Create(1, bpp, 1); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Pixel2: + info = PixelComponentInfo.Create(1, bpp, 2); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Pixel4: + info = PixelComponentInfo.Create(1, bpp, 4); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Pixel8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Indexed; + break; // Could be 555 with padding but 565 is more common in newer bitmaps and offers // greater accuracy due to extra green precision. - BmpBitsPerPixel.Pixel16 => PixelComponentInfo.Create(3, bpp, 5, 6, 5), - BmpBitsPerPixel.Pixel24 => PixelComponentInfo.Create(3, bpp, 8, 8, 8), - BmpBitsPerPixel.Pixel32 or _ => PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8), - }; - - PixelColorType color = this.BitsPerPixel switch - { - BmpBitsPerPixel.Pixel1 or - BmpBitsPerPixel.Pixel2 or - BmpBitsPerPixel.Pixel4 or - BmpBitsPerPixel.Pixel8 => PixelColorType.Indexed, - BmpBitsPerPixel.Pixel16 or - BmpBitsPerPixel.Pixel24 => PixelColorType.RGB, - BmpBitsPerPixel.Pixel32 or _ => PixelColorType.RGB | PixelColorType.Alpha, - }; + case BmpBitsPerPixel.Pixel16: + info = PixelComponentInfo.Create(3, bpp, 5, 6, 5); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Pixel24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Pixel32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + break; + } return new() { diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 9a234aa9b..64c7edbaf 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -78,11 +78,13 @@ public class GifMetadata : IFormatMetadata ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; for (int i = 0; i < colorTable.Length; i++) { - if (background == colorTable[i]) + if (background != colorTable[i]) { - index = i; - break; + continue; } + + index = i; + break; } } @@ -105,11 +107,13 @@ public class GifMetadata : IFormatMetadata ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; for (int i = 0; i < colorTable.Length; i++) { - if (background == colorTable[i]) + if (background != colorTable[i]) { - index = i; - break; + continue; } + + index = i; + break; } } diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 4e6b6f265..49a13d5c3 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -40,7 +40,7 @@ public class PbmMetadata : IFormatMetadata /// /// Gets or sets the data type of the pixel components. /// - public PbmComponentType ComponentType { get; set; } = PbmComponentType.Byte; + public PbmComponentType ComponentType { get; set; } /// public static PbmMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) @@ -69,16 +69,13 @@ public class PbmMetadata : IFormatMetadata break; } - PbmComponentType componentType = PbmComponentType.Short; int bpp = metadata.PixelTypeInfo.BitsPerPixel; - if (bpp == 1) + PbmComponentType componentType = bpp switch { - componentType = PbmComponentType.Bit; - } - else if (bpp <= 8) - { - componentType = PbmComponentType.Byte; - } + 1 => PbmComponentType.Bit, + <= 8 => PbmComponentType.Byte, + _ => PbmComponentType.Short + }; return new PbmMetadata { From fdcd60200e904a05a1f34d76885a238d2f1e4471 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 5 Jun 2024 22:04:16 +1000 Subject: [PATCH 181/220] Begin migrating TiffMetadata --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 29 ++- src/ImageSharp/Formats/Gif/GifMetadata.cs | 24 +- src/ImageSharp/Formats/IFormatMetadata.cs | 8 + src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 22 +- src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 20 +- src/ImageSharp/Formats/Png/PngMetadata.cs | 22 +- src/ImageSharp/Formats/Qoi/QoiMetadata.cs | 20 +- src/ImageSharp/Formats/Tga/TgaMetadata.cs | 21 +- .../Formats/Tiff/Constants/TiffConstants.cs | 45 +++- .../Formats/Tiff/Ifd/EntryReader.cs | 2 +- .../Formats/Tiff/TiffBitsPerSample.cs | 8 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 78 +----- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- .../Formats/Tiff/TiffEncoderCore.cs | 239 ++++++------------ .../Formats/Tiff/TiffFrameMetadata.cs | 13 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 18 +- .../Formats/Tiff/TiffEncoderBaseTester.cs | 2 +- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 4 +- .../Formats/Tiff/TiffEncoderTests.cs | 36 +-- .../TestUtilities/TestImageExtensions.cs | 17 -- 20 files changed, 267 insertions(+), 363 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index bb6b87e58..d44520a4f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -50,15 +50,18 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }, <= 16 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 + BitsPerPixel = BmpBitsPerPixel.Pixel16, + InfoHeaderType = BmpInfoHeaderType.WinVersion3 }, <= 24 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 + BitsPerPixel = BmpBitsPerPixel.Pixel24, + InfoHeaderType = BmpInfoHeaderType.WinVersion4 }, _ => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 + BitsPerPixel = BmpBitsPerPixel.Pixel32, + InfoHeaderType = BmpInfoHeaderType.WinVersion5 } }; } @@ -68,7 +71,7 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new(); /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp = (int)this.BitsPerPixel; @@ -122,17 +125,21 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() => new(); diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 64c7edbaf..90d3312c8 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -126,6 +126,20 @@ public class GifMetadata : IFormatMetadata }; } + /// + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp = this.GlobalColorTable.HasValue + ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.GlobalColorTable.Value.Length), 1, 8) + : 8; + + return new PixelTypeInfo(bpp) + { + ColorType = PixelColorType.Indexed, + ComponentInfo = PixelComponentInfo.Create(1, bpp, bpp), + }; + } + /// public FormatConnectingMetadata ToFormatConnectingMetadata() { @@ -133,21 +147,13 @@ public class GifMetadata : IFormatMetadata ? this.GlobalColorTable.Value.Span[this.BackgroundColorIndex] : Color.Transparent; - int bpp = this.GlobalColorTable.HasValue - ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.GlobalColorTable.Value.Length), 1, 8) - : 8; - return new() { AnimateRootFrame = true, BackgroundColor = color, ColorTable = this.GlobalColorTable, ColorTableMode = this.ColorTableMode, - PixelTypeInfo = new PixelTypeInfo(bpp) - { - ColorType = PixelColorType.Indexed, - ComponentInfo = PixelComponentInfo.Create(1, bpp, bpp), - }, + PixelTypeInfo = this.GetPixelTypeInfo(), RepeatCount = this.RepeatCount, }; } diff --git a/src/ImageSharp/Formats/IFormatMetadata.cs b/src/ImageSharp/Formats/IFormatMetadata.cs index f542f92db..e0c32e8b2 100644 --- a/src/ImageSharp/Formats/IFormatMetadata.cs +++ b/src/ImageSharp/Formats/IFormatMetadata.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats; /// @@ -8,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats; /// public interface IFormatMetadata : IDeepCloneable { + /// + /// Converts the metadata to a instance. + /// + /// The pixel type info. + PixelTypeInfo GetPixelTypeInfo(); + /// /// Converts the metadata to a instance. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index b12ee524d..d15abafde 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -139,7 +139,7 @@ public class JpegMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -173,18 +173,22 @@ public class JpegMetadata : IFormatMetadata break; } - return new FormatConnectingMetadata + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = PixelAlphaRepresentation.None, - ColorType = colorType, - ComponentInfo = info, - }, - Quality = this.Quality, + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + Quality = this.Quality, + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 49a13d5c3..49a36de41 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -85,7 +85,7 @@ public class PbmMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -114,17 +114,21 @@ public class PbmMetadata : IFormatMetadata break; } - return new FormatConnectingMetadata + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = PixelAlphaRepresentation.None, - ColorType = colorType, - ComponentInfo = info, - }, + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index d789ae8fa..d72dc5184 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -191,7 +191,7 @@ public class PngMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -262,19 +262,23 @@ public class PngMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() { ColorTable = this.ColorTable, ColorTableMode = FrameColorTableMode.Global, - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ColorType = colorType, - ComponentInfo = info, - }, + PixelTypeInfo = this.GetPixelTypeInfo(), RepeatCount = (ushort)Numerics.Clamp(this.RepeatCount, 0, ushort.MaxValue), }; - } /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs index 4be714fc3..c8bcd9748 100644 --- a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs +++ b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs @@ -51,7 +51,7 @@ public class QoiMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -73,17 +73,21 @@ public class QoiMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ColorType = colorType, - ComponentInfo = info, - } + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs index 345d1698e..fa8e8b6f4 100644 --- a/src/ImageSharp/Formats/Tga/TgaMetadata.cs +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -49,10 +49,9 @@ public class TgaMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp = (int)this.BitsPerPixel; - PixelComponentInfo info; PixelColorType color; PixelAlphaRepresentation alpha; @@ -80,17 +79,21 @@ public class TgaMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ComponentInfo = info, - ColorType = color - } + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 978860910..c24eee484 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -39,9 +39,9 @@ internal static class TiffConstants public const ushort BigTiffHeaderMagicNumber = 43; /// - /// The big tiff bytesize of offsets value. + /// The big tiff byte size of offsets value. /// - public const ushort BigTiffBytesize = 8; + public const ushort BigTiffByteSize = 8; /// /// RowsPerStrip default value, which is effectively infinity. @@ -58,38 +58,63 @@ internal static class TiffConstants /// public const int DefaultStripSize = 8 * 1024; + /// + /// The default predictor is None. + /// + public const TiffPredictor DefaultPredictor = TiffPredictor.None; + + /// + /// The default bits per pixel is Bit24. + /// + public const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24; + + /// + /// The default bits per sample for color images with 8 bits for each color channel. + /// + public static readonly TiffBitsPerSample DefaultBitsPerSample = BitsPerSampleRgb8Bit; + + /// + /// The default compression is None. + /// + public const TiffCompression DefaultCompression = TiffCompression.None; + + /// + /// The default photometric interpretation is Rgb. + /// + public const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + /// /// The bits per sample for 1 bit bicolor images. /// - public static readonly TiffBitsPerSample BitsPerSample1Bit = new TiffBitsPerSample(1, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample1Bit = new(1, 0, 0); /// /// The bits per sample for images with a 4 color palette. /// - public static readonly TiffBitsPerSample BitsPerSample4Bit = new TiffBitsPerSample(4, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample4Bit = new(4, 0, 0); /// /// The bits per sample for 8 bit images. /// - public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample8Bit = new(8, 0, 0); /// /// The bits per sample for 16-bit grayscale images. /// - public static readonly TiffBitsPerSample BitsPerSample16Bit = new TiffBitsPerSample(16, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample16Bit = new(16, 0, 0); /// /// The bits per sample for color images with 8 bits for each color channel. /// - public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new TiffBitsPerSample(8, 8, 8); + public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new(8, 8, 8); /// - /// The list of mimetypes that equate to a tiff. + /// The list of mime types that equate to a tiff. /// - public static readonly IEnumerable MimeTypes = new[] { "image/tiff", "image/tiff-fx" }; + public static readonly IEnumerable MimeTypes = ["image/tiff", "image/tiff-fx"]; /// /// The list of file extensions that equate to a tiff. /// - public static readonly IEnumerable FileExtensions = new[] { "tiff", "tif" }; + public static readonly IEnumerable FileExtensions = ["tiff", "tif"]; } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 267370009..4496de6fb 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -64,7 +64,7 @@ internal class HeaderReader : BaseExifReader ushort bytesize = this.ReadUInt16(); ushort reserve = this.ReadUInt16(); - if (bytesize == TiffConstants.BigTiffBytesize && reserve == 0) + if (bytesize == TiffConstants.BigTiffByteSize && reserve == 0) { this.FirstIfdOffset = this.ReadUInt64(); return; diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index 382faa387..2bfd9a626 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -147,20 +147,20 @@ public readonly struct TiffBitsPerSample : IEquatable { if (this.Channel1 == 0) { - return new[] { this.Channel0 }; + return [this.Channel0]; } if (this.Channel2 == 0) { - return new[] { this.Channel0, this.Channel1 }; + return [this.Channel0, this.Channel1]; } if (this.Channel3 == 0) { - return new[] { this.Channel0, this.Channel1, this.Channel2 }; + return [this.Channel0, this.Channel1, this.Channel2]; } - return new[] { this.Channel0, this.Channel1, this.Channel2, this.Channel3 }; + return [this.Channel0, this.Channel1, this.Channel2, this.Channel3]; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 5a5c2b3e5..186a4bd31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -63,7 +63,7 @@ internal static class TiffDecoderOptionsParser } TiffSampleFormat? sampleFormat = null; - if (exifProfile.TryGetValue(ExifTag.SampleFormat, out var formatValue)) + if (exifProfile.TryGetValue(ExifTag.SampleFormat, out IExifValue formatValue)) { TiffSampleFormat[] sampleFormats = formatValue.Value.Select(a => (TiffSampleFormat)a).ToArray(); sampleFormat = sampleFormats[0]; @@ -106,11 +106,11 @@ internal static class TiffDecoderOptionsParser options.PlanarConfiguration = DefaultPlanarConfiguration; } - options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; - options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; + options.Predictor = frameMetadata.Predictor; + options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation; options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger; - options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; - options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0); + options.BitsPerPixel = (int)frameMetadata.BitsPerPixel; + options.BitsPerSample = frameMetadata.BitsPerSample; if (exifProfile.TryGetValue(ExifTag.ReferenceBlackWhite, out IExifValue blackWhiteValue)) { @@ -142,9 +142,7 @@ internal static class TiffDecoderOptionsParser options.ParseCompression(frameMetadata.Compression, exifProfile); options.ParseColorType(exifProfile); - bool isTiled = VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration); - - return isTiled; + return VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration); } /// @@ -194,13 +192,6 @@ internal static class TiffDecoderOptionsParser } } - // For BiColor compressed images, the BitsPerPixel value will be set explicitly to 1, so we don't throw in those cases. - // See: https://github.com/SixLabors/ImageSharp/issues/2587 - if (frameMetadata.BitsPerPixel == null && !IsBiColorCompression(frameMetadata.Compression)) - { - TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); - } - return isTiled; } @@ -224,7 +215,6 @@ internal static class TiffDecoderOptionsParser switch (bitsPerChannel) { case 32: - { if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.WhiteIsZero32Float; @@ -233,43 +223,30 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.WhiteIsZero32; break; - } case 24: - { options.ColorType = TiffColorType.WhiteIsZero24; break; - } case 16: - { options.ColorType = TiffColorType.WhiteIsZero16; break; - } case 8: - { options.ColorType = TiffColorType.WhiteIsZero8; break; - } case 4: - { options.ColorType = TiffColorType.WhiteIsZero4; break; - } case 1: - { options.ColorType = TiffColorType.WhiteIsZero1; break; - } default: - { options.ColorType = TiffColorType.WhiteIsZero; break; - } } break; @@ -291,7 +268,6 @@ internal static class TiffDecoderOptionsParser switch (bitsPerChannel) { case 32: - { if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.BlackIsZero32Float; @@ -300,43 +276,30 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.BlackIsZero32; break; - } case 24: - { options.ColorType = TiffColorType.BlackIsZero24; break; - } case 16: - { options.ColorType = TiffColorType.BlackIsZero16; break; - } case 8: - { options.ColorType = TiffColorType.BlackIsZero8; break; - } case 4: - { options.ColorType = TiffColorType.BlackIsZero4; break; - } case 1: - { options.ColorType = TiffColorType.BlackIsZero1; break; - } default: - { options.ColorType = TiffColorType.BlackIsZero; break; - } } break; @@ -535,29 +498,21 @@ internal static class TiffDecoderOptionsParser switch (compression ?? TiffCompression.None) { case TiffCompression.None: - { options.CompressionType = TiffDecoderCompressionType.None; break; - } case TiffCompression.PackBits: - { options.CompressionType = TiffDecoderCompressionType.PackBits; break; - } case TiffCompression.Deflate: case TiffCompression.OldDeflate: - { options.CompressionType = TiffDecoderCompressionType.Deflate; break; - } case TiffCompression.Lzw: - { options.CompressionType = TiffDecoderCompressionType.Lzw; break; - } case TiffCompression.CcittGroup3Fax: { @@ -599,16 +554,13 @@ internal static class TiffDecoderOptionsParser } case TiffCompression.Ccitt1D: - { options.CompressionType = TiffDecoderCompressionType.HuffmanRle; options.BitsPerSample = new TiffBitsPerSample(1, 0, 0); options.BitsPerPixel = 1; break; - } case TiffCompression.OldJpeg: - { if (!options.OldJpegCompressionStartOfImageMarker.HasValue) { TiffThrowHelper.ThrowNotSupported("Missing SOI marker offset for tiff with old jpeg compression"); @@ -629,10 +581,8 @@ internal static class TiffDecoderOptionsParser } break; - } case TiffCompression.Jpeg: - { options.CompressionType = TiffDecoderCompressionType.Jpeg; if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) @@ -643,30 +593,14 @@ internal static class TiffDecoderOptionsParser } break; - } case TiffCompression.Webp: - { options.CompressionType = TiffDecoderCompressionType.Webp; break; - } default: - { TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported"); break; - } } } - - private static bool IsBiColorCompression(TiffCompression? compression) - { - if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or - TiffCompression.CcittGroup4Fax) - { - return true; - } - - return false; - } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index ea64e8289..a068613bf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -47,7 +47,7 @@ public class TiffEncoder : QuantizingImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - TiffEncoderCore encode = new(this, image.Configuration.MemoryAllocator); + TiffEncoderCore encode = new(this, image.Configuration); encode.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 149f23f1b..c702252cc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable -using SixLabors.ImageSharp.Advanced; +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; @@ -50,41 +49,22 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// private readonly DeflateCompressionLevel compressionLevel; - /// - /// The default predictor is None. - /// - private const TiffPredictor DefaultPredictor = TiffPredictor.None; - - /// - /// The default bits per pixel is Bit24. - /// - private const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24; - - /// - /// The default compression is None. - /// - private const TiffCompression DefaultCompression = TiffCompression.None; - - /// - /// The default photometric interpretation is Rgb. - /// - private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - /// /// Whether to skip metadata during encoding. /// private readonly bool skipMetadata; - private readonly List<(long, uint)> frameMarkers = new(); + private readonly List<(long, uint)> frameMarkers = []; /// /// Initializes a new instance of the class. /// /// The options for the encoder. - /// The memory allocator. - public TiffEncoderCore(TiffEncoder options, MemoryAllocator memoryAllocator) + /// The global configuration. + public TiffEncoderCore(TiffEncoder options, Configuration configuration) { - this.memoryAllocator = memoryAllocator; + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; this.PhotometricInterpretation = options.PhotometricInterpretation; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = options.PixelSamplingStrategy; @@ -135,35 +115,29 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals // Determine the correct values to encode with. // EncoderOptions > Metadata > Default. - TiffBitsPerPixel? bitsPerPixel = this.BitsPerPixel ?? rootFrameTiffMetaData.BitsPerPixel; + TiffBitsPerPixel bitsPerPixel = this.BitsPerPixel ?? rootFrameTiffMetaData.BitsPerPixel; - TiffPhotometricInterpretation? photometricInterpretation = this.PhotometricInterpretation ?? rootFrameTiffMetaData.PhotometricInterpretation; + TiffPhotometricInterpretation photometricInterpretation = this.PhotometricInterpretation ?? rootFrameTiffMetaData.PhotometricInterpretation; - TiffPredictor predictor = - this.HorizontalPredictor - ?? rootFrameTiffMetaData.Predictor - ?? DefaultPredictor; + TiffPredictor predictor = this.HorizontalPredictor ?? rootFrameTiffMetaData.Predictor; - TiffCompression compression = - this.CompressionType - ?? rootFrameTiffMetaData.Compression - ?? DefaultCompression; + TiffCompression compression = this.CompressionType ?? rootFrameTiffMetaData.Compression; - // Make sure, the Encoder options makes sense in combination with each other. - this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); + // Make sure the Encoder options makes sense in combination with each other. + this.SanitizeAndSetEncoderOptions(bitsPerPixel, photometricInterpretation, compression, predictor); using TiffStreamWriter writer = new(stream); Span buffer = stackalloc byte[4]; long ifdMarker = WriteHeader(writer, buffer); - Image metadataImage = image; + Image? metadataImage = image; foreach (ImageFrame frame in image.Frames) { cancellationToken.ThrowIfCancellationRequested(); - ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker); + ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, this.BitsPerPixel.Value, this.CompressionType.Value, ifdMarker); metadataImage = null; } @@ -199,6 +173,8 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// The tiff frame. /// The image metadata (resolution values for each frame). /// The image (common metadata for root frame). + /// The bits per pixel. + /// The compression type. /// The marker to write this IFD offset. /// /// The next IFD offset value. @@ -207,16 +183,18 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals TiffStreamWriter writer, ImageFrame frame, ImageMetadata imageMetadata, - Image image, + Image? image, + TiffBitsPerPixel bitsPerPixel, + TiffCompression compression, long ifdOffset) where TPixel : unmanaged, IPixel { using TiffBaseCompressor compressor = TiffCompressorFactory.Create( - this.CompressionType ?? TiffCompression.None, + compression, writer.BaseStream, this.memoryAllocator, frame.Width, - (int)this.BitsPerPixel, + (int)bitsPerPixel, this.compressionLevel, this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None); @@ -229,7 +207,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals this.memoryAllocator, this.configuration, entriesCollector, - (int)this.BitsPerPixel); + (int)bitsPerPixel); int rowsPerStrip = CalcRowsPerStrip(frame.Height, colorWriter.BytesPerRow, this.CompressionType); @@ -307,7 +285,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals } uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); - List largeDataBlocks = new(); + List largeDataBlocks = []; entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag); @@ -354,135 +332,80 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals return nextIfdMarker; } + [MemberNotNull(nameof(BitsPerPixel), nameof(PhotometricInterpretation), nameof(CompressionType), nameof(HorizontalPredictor))] private void SanitizeAndSetEncoderOptions( - TiffBitsPerPixel? bitsPerPixel, - int inputBitsPerPixel, - TiffPhotometricInterpretation? photometricInterpretation, + TiffBitsPerPixel bitsPerPixel, + TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression, TiffPredictor predictor) { // BitsPerPixel should be the primary source of truth for the encoder options. - if (bitsPerPixel.HasValue) - { - switch (bitsPerPixel) - { - case TiffBitsPerPixel.Bit1: - if (IsOneBitCompression(compression)) - { - // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); - break; - } - - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); - break; - case TiffBitsPerPixel.Bit4: - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); - break; - 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.Bit30: - case TiffBitsPerPixel.Bit36: - case TiffBitsPerPixel.Bit42: - case TiffBitsPerPixel.Bit48: - // Encoding not yet supported bits per pixel will default to 24 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); - break; - case TiffBitsPerPixel.Bit64: - // Encoding not yet supported bits per pixel will default to 32 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); - break; - default: - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); - break; - } - - // Make sure 1 Bit compression is only used with 1 bit pixel type. - if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) - { - // Invalid compression / bits per pixel combination, fallback to no compression. - this.CompressionType = DefaultCompression; - } - - return; - } - - // If no photometric interpretation was chosen, the input image bit per pixel should be preserved. - if (!photometricInterpretation.HasValue) + switch (bitsPerPixel) { - if (IsOneBitCompression(this.CompressionType)) - { - // We need to make sure bits per pixel is set to Bit1 now. WhiteIsZero is set because its the default for bilevel compressed data. - this.SetEncoderOptions(TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); - return; - } - - // 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; - } - - switch (photometricInterpretation) - { - case TiffPhotometricInterpretation.BlackIsZero: - case TiffPhotometricInterpretation.WhiteIsZero: - if (IsOneBitCompression(this.CompressionType)) - { - this.SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); - return; - } - - if (inputBitsPerPixel == 16) + case TiffBitsPerPixel.Bit1: + if (IsOneBitCompression(compression)) { - this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor); - return; + // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); + break; } - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - - case TiffPhotometricInterpretation.PaletteColor: - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - - case TiffPhotometricInterpretation.Rgb: - // Make sure 1 Bit compression is only used with 1 bit pixel type. - if (IsOneBitCompression(this.CompressionType)) + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit4: + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit8: + + // Allow any combination of the below for 8 bit images. + if (photometricInterpretation is TiffPhotometricInterpretation.BlackIsZero + or TiffPhotometricInterpretation.WhiteIsZero + or TiffPhotometricInterpretation.PaletteColor) { - // Invalid compression / bits per pixel combination, fallback to no compression. - compression = DefaultCompression; + this.SetEncoderOptions(bitsPerPixel, photometricInterpretation, compression, predictor); + break; } - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, photometricInterpretation, compression, predictor); - return; + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, 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.Bit30: + case TiffBitsPerPixel.Bit36: + case TiffBitsPerPixel.Bit42: + case TiffBitsPerPixel.Bit48: + // Encoding not yet supported bits per pixel will default to 24 bits. + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit64: + // Encoding not yet supported bits per pixel will default to 32 bits. + this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + break; + default: + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); + break; } - this.SetEncoderOptions(DefaultBitsPerPixel, DefaultPhotometricInterpretation, compression, predictor); + // Make sure 1 Bit compression is only used with 1 bit pixel type. + if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) + { + // Invalid compression / bits per pixel combination, fallback to no compression. + this.CompressionType = TiffCompression.None; + } } - private void SetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) + [MemberNotNull(nameof(BitsPerPixel), nameof(PhotometricInterpretation), nameof(CompressionType), nameof(HorizontalPredictor))] + private void SetEncoderOptions( + TiffBitsPerPixel bitsPerPixel, + TiffPhotometricInterpretation photometricInterpretation, + TiffCompression compression, + TiffPredictor predictor) { this.BitsPerPixel = bitsPerPixel; this.PhotometricInterpretation = photometricInterpretation; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index e30983098..114fe703c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -34,27 +34,27 @@ public class TiffFrameMetadata : IDeepCloneable /// /// Gets or sets the bits per pixel. /// - public TiffBitsPerPixel? BitsPerPixel { get; set; } + public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffConstants.DefaultBitsPerPixel; /// /// Gets or sets number of bits per component. /// - public TiffBitsPerSample? BitsPerSample { get; set; } + public TiffBitsPerSample BitsPerSample { get; set; } = TiffConstants.DefaultBitsPerSample; /// /// Gets or sets the compression scheme used on the image data. /// - public TiffCompression? Compression { get; set; } + public TiffCompression Compression { get; set; } = TiffConstants.DefaultCompression; /// /// Gets or sets the color space of the image data. /// - public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; } + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } = TiffConstants.DefaultPhotometricInterpretation; /// /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied. /// - public TiffPredictor? Predictor { get; set; } + public TiffPredictor Predictor { get; set; } = TiffConstants.DefaultPredictor; /// /// Gets or sets the set of inks used in a separated () image. @@ -89,7 +89,7 @@ public class TiffFrameMetadata : IDeepCloneable meta.BitsPerSample = bitsPerSample; } - meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); + meta.BitsPerPixel = meta.BitsPerSample.BitsPerPixel(); if (profile.TryGetValue(ExifTag.Compression, out IExifValue? compressionValue)) { @@ -111,6 +111,7 @@ public class TiffFrameMetadata : IDeepCloneable meta.InkSet = (TiffInkSet)inkSetValue.Value; } + // TODO: Why do we remove this? Encoding should overwrite. profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.PhotometricInterpretation); diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 2759d0130..b95b9f92f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Provides Tiff specific metadata information for the image. /// -public class TiffMetadata : IDeepCloneable +public class TiffMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -36,5 +38,17 @@ public class TiffMetadata : IDeepCloneable public TiffFormatType FormatType { get; set; } /// - public IDeepCloneable DeepClone() => new TiffMetadata(this); + public static TiffMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException(); + + /// + public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException(); + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public TiffMetadata DeepClone() => new(this); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs index 2a822e705..0cff35217 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs @@ -30,7 +30,7 @@ public abstract class TiffEncoderBaseTester using Image input = provider.GetImage(); using var memStream = new MemoryStream(); TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); - TiffCompression inputCompression = inputMeta.Compression ?? TiffCompression.None; + TiffCompression inputCompression = inputMeta.Compression; // act input.Save(memStream, tiffEncoder); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 872414730..282966ea8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -15,7 +15,7 @@ public class TiffEncoderHeaderTests public void WriteHeader_WritesValidHeader() { using MemoryStream stream = new(); - TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); + TiffEncoderCore encoder = new(Encoder, Configuration.Default); using (TiffStreamWriter writer = new(stream)) { @@ -29,7 +29,7 @@ public class TiffEncoderHeaderTests public void WriteHeader_ReturnsFirstIfdMarker() { using MemoryStream stream = new(); - TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); + TiffEncoderCore encoder = new(Encoder, Configuration.Default); using TiffStreamWriter writer = new(stream); long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 1fafb4cd0..197210116 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -31,7 +31,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel expectedBitsPerPixel) { // arrange - TiffEncoder tiffEncoder = new() { PhotometricInterpretation = photometricInterpretation }; + TiffEncoder tiffEncoder = new() { BitsPerPixel = expectedBitsPerPixel, PhotometricInterpretation = photometricInterpretation }; using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 ? new Image(10, 10) : new Image(10, 10); @@ -57,8 +57,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel) { // arrange - TiffEncoder tiffEncoder = new() - { BitsPerPixel = bitsPerPixel }; + TiffEncoder tiffEncoder = new() { BitsPerPixel = bitsPerPixel }; using Image input = new Image(10, 10); using MemoryStream memStream = new(); @@ -156,7 +155,11 @@ public class TiffEncoderTests : TiffEncoderBaseTester { // arrange TiffEncoder tiffEncoder = new() - { PhotometricInterpretation = photometricInterpretation, Compression = compression }; + { + BitsPerPixel = expectedBitsPerPixel, + PhotometricInterpretation = photometricInterpretation, + Compression = compression + }; using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 ? new Image(10, 10) : new Image(10, 10); @@ -199,25 +202,6 @@ public class TiffEncoderTests : TiffEncoderBaseTester Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } - [Fact] - public void TiffEncoder_PreservesBitsPerPixel_WhenInputIsL8() - { - // arrange - TiffEncoder tiffEncoder = new(); - using Image input = new Image(10, 10); - using MemoryStream memStream = new(); - const TiffBitsPerPixel expectedBitsPerPixel = TiffBitsPerPixel.Bit8; - - // act - input.Save(memStream, tiffEncoder); - - // assert - memStream.Position = 0; - using Image output = Image.Load(memStream); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); - } - [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.None)] [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffCompression.Lzw)] @@ -241,11 +225,11 @@ public class TiffEncoderTests : TiffEncoderBaseTester } [Theory] - [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, null)] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffPredictor.None)] [WithFile(RgbLzwPredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] - [WithFile(RgbDeflate, PixelTypes.Rgba32, null)] + [WithFile(RgbDeflate, PixelTypes.Rgba32, TiffPredictor.None)] [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] - public void TiffEncoder_PreservesPredictor(TestImageProvider provider, TiffPredictor? expectedPredictor) + public void TiffEncoder_PreservesPredictor(TestImageProvider provider, TiffPredictor expectedPredictor) where TPixel : unmanaged, IPixel { // arrange diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 5da12f264..05abedbd8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -697,23 +697,6 @@ public static class TestImageExtensions return new AllocatorBufferCapacityConfigurator(allocator, Unsafe.SizeOf()); } - internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) - { - Image image = new(buffer.Width, buffer.Height); - - Assert.True(image.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out Memory pixelMem)); - Span pixels = pixelMem.Span; - Span bufferSpan = buffer.DangerousGetSingleSpan(); - - for (int i = 0; i < bufferSpan.Length; i++) - { - float value = bufferSpan[i] * scale; - pixels[i] = Rgba32.FromVector4(new Vector4(value, value, value, 1f)); - } - - return image; - } - private class MakeOpaqueProcessor : IImageProcessor { public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) From 1a9d578a04f0d48740d361eb33bec20256750267 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 6 Jun 2024 21:31:43 +1000 Subject: [PATCH 182/220] Migrate TiffMetadata --- .../Tiff/TiffDecoderMetadataCreator.cs | 16 +- .../Formats/Tiff/TiffEncoderCore.cs | 35 +++-- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 140 +++++++++++++++++- 3 files changed, 172 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 1ef2478e3..28565cac4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -23,7 +23,7 @@ internal static class TiffDecoderMetadataCreator TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } - ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0].ExifProfile); + ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0]); if (!ignoreMetadata) { @@ -50,14 +50,22 @@ internal static class TiffDecoderMetadataCreator return imageMetaData; } - private static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) + private static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ImageFrameMetadata rootFrameMetadata) { ImageMetadata imageMetaData = new(); - SetResolution(imageMetaData, exifProfile); + SetResolution(imageMetaData, rootFrameMetadata.ExifProfile); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; tiffMetadata.FormatType = isBigTiff ? TiffFormatType.BigTIFF : TiffFormatType.Default; + + TiffFrameMetadata tiffFrameMetadata = rootFrameMetadata.GetTiffMetadata(); + tiffMetadata.BitsPerPixel = tiffFrameMetadata.BitsPerPixel; + tiffMetadata.BitsPerSample = tiffFrameMetadata.BitsPerSample; + tiffMetadata.Compression = tiffFrameMetadata.Compression; + tiffMetadata.PhotometricInterpretation = tiffFrameMetadata.PhotometricInterpretation; + tiffMetadata.Predictor = tiffFrameMetadata.Predictor; + return imageMetaData; } @@ -109,7 +117,7 @@ internal static class TiffDecoderMetadataCreator return false; } - // Probably wrong endianess, swap byte order. + // Probably wrong endianness, swap byte order. Span iptcBytesSpan = iptcBytes.AsSpan(); Span buffer = stackalloc byte[4]; for (int i = 0; i < iptcBytes.Length; i += 4) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index c702252cc..546ff7947 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -339,6 +339,20 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals TiffCompression compression, TiffPredictor predictor) { + // Ensure 1 Bit compression is only used with 1 bit pixel type. + // Choose a sensible default based on the bits per pixel. + if (IsOneBitCompression(compression) && bitsPerPixel != TiffBitsPerPixel.Bit1) + { + compression = bitsPerPixel switch + { + < TiffBitsPerPixel.Bit8 => TiffCompression.None, + _ => TiffCompression.Deflate, + }; + } + + // Ensure predictor is only used with compression that supports it. + predictor = HasPredictor(compression) ? predictor : TiffPredictor.None; + // BitsPerPixel should be the primary source of truth for the encoder options. switch (bitsPerPixel) { @@ -346,14 +360,14 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals if (IsOneBitCompression(compression)) { // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, predictor); break; } - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); break; case TiffBitsPerPixel.Bit4: - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, predictor); break; case TiffBitsPerPixel.Bit8: @@ -381,23 +395,17 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit48: // Encoding not yet supported bits per pixel will default to 24 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); break; case TiffBitsPerPixel.Bit64: // Encoding not yet supported bits per pixel will default to 32 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, predictor); break; default: this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); break; } - - // Make sure 1 Bit compression is only used with 1 bit pixel type. - if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) - { - // Invalid compression / bits per pixel combination, fallback to no compression. - this.CompressionType = TiffCompression.None; - } } [MemberNotNull(nameof(BitsPerPixel), nameof(PhotometricInterpretation), nameof(CompressionType), nameof(HorizontalPredictor))] @@ -415,4 +423,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals public static bool IsOneBitCompression(TiffCompression? compression) => compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax; + + public static bool HasPredictor(TiffCompression? compression) + => compression is TiffCompression.Deflate or TiffCompression.Lzw; } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index b95b9f92f..cc70941d5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff; @@ -25,6 +26,11 @@ public class TiffMetadata : IFormatMetadata { this.ByteOrder = other.ByteOrder; this.FormatType = other.FormatType; + this.BitsPerPixel = other.BitsPerPixel; + this.BitsPerSample = other.BitsPerSample; + this.Compression = other.Compression; + this.PhotometricInterpretation = other.PhotometricInterpretation; + this.Predictor = other.Predictor; } /// @@ -37,14 +43,142 @@ public class TiffMetadata : IFormatMetadata /// public TiffFormatType FormatType { get; set; } + /// + /// Gets or sets the bits per pixel. Derived from the root frame. + /// + public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffConstants.DefaultBitsPerPixel; + + /// + /// Gets or sets number of bits per component. Derived from the root frame. + /// + public TiffBitsPerSample BitsPerSample { get; set; } = TiffConstants.DefaultBitsPerSample; + + /// + /// Gets or sets the compression scheme used on the image data. Derived from the root frame. + /// + public TiffCompression Compression { get; set; } = TiffConstants.DefaultCompression; + + /// + /// Gets or sets the color space of the image data. Derived from the root frame. + /// + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } = TiffConstants.DefaultPhotometricInterpretation; + + /// + /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied. + /// Derived from the root frame. + /// + public TiffPredictor Predictor { get; set; } = TiffConstants.DefaultPredictor; + /// - public static TiffMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException(); + public static TiffMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + return bpp switch + { + 1 => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit1, + BitsPerSample = TiffConstants.BitsPerSample1Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero, + Compression = TiffCompression.CcittGroup4Fax, + Predictor = TiffPredictor.None + }, + <= 4 => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit4, + BitsPerSample = TiffConstants.BitsPerSample4Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor, + Compression = TiffCompression.Deflate, + Predictor = TiffPredictor.None // Best match for low bit depth + }, + 8 => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit8, + BitsPerSample = TiffConstants.BitsPerSample8Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor, + Compression = TiffCompression.Deflate, + Predictor = TiffPredictor.Horizontal + }, + 16 => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit16, + BitsPerSample = TiffConstants.BitsPerSample16Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero, + Compression = TiffCompression.Deflate, + Predictor = TiffPredictor.Horizontal + }, + 32 or 64 => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit32, + BitsPerSample = TiffConstants.BitsPerSampleRgb8Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.Rgb, + Compression = TiffCompression.Deflate, + Predictor = TiffPredictor.Horizontal + }, + _ => new TiffMetadata + { + BitsPerPixel = TiffBitsPerPixel.Bit24, + BitsPerSample = TiffConstants.BitsPerSampleRgb8Bit, + PhotometricInterpretation = TiffPhotometricInterpretation.Rgb, + Compression = TiffCompression.Deflate, + Predictor = TiffPredictor.Horizontal + } + }; + } /// - public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException(); + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp = (int)this.BitsPerPixel; + + TiffBitsPerSample samples = this.BitsPerSample; + PixelComponentInfo info = samples.Channels switch + { + 1 => PixelComponentInfo.Create(1, bpp, bpp), + 2 => PixelComponentInfo.Create(2, bpp, bpp, samples.Channel0, samples.Channel1), + 3 => PixelComponentInfo.Create(3, bpp, samples.Channel0, samples.Channel1, samples.Channel2), + _ => PixelComponentInfo.Create(4, bpp, samples.Channel0, samples.Channel1, samples.Channel2, samples.Channel3) + }; + + PixelColorType colorType; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + switch (this.BitsPerPixel) + { + case TiffBitsPerPixel.Bit1: + colorType = PixelColorType.Binary; + break; + case TiffBitsPerPixel.Bit4: + case TiffBitsPerPixel.Bit6: + case TiffBitsPerPixel.Bit8: + colorType = PixelColorType.Indexed; + break; + case TiffBitsPerPixel.Bit16: + colorType = PixelColorType.Luminance; + break; + case TiffBitsPerPixel.Bit32: + case TiffBitsPerPixel.Bit64: + colorType = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + default: + colorType = PixelColorType.RGB; + break; + } + + return new PixelTypeInfo(bpp) + { + ColorType = colorType, + ComponentInfo = info, + AlphaRepresentation = alpha + }; + } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException(); + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); From d371a62e1651cf0f834a5bae70ddfc867a53da06 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 6 Jun 2024 22:32:46 +1000 Subject: [PATCH 183/220] Migrate TiffFrameMetadata --- .../Formats/Tiff/TiffFrameMetadata.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 114fe703c..bb5da3741 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Provides Tiff specific metadata information for the frame. /// -public class TiffFrameMetadata : IDeepCloneable +public class TiffFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -61,6 +61,20 @@ public class TiffFrameMetadata : IDeepCloneable /// public TiffInkSet? InkSet { get; set; } + /// + public static TiffFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + => new(); + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => new(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public TiffFrameMetadata DeepClone() => new(this); + /// /// Returns a new instance parsed from the given Exif profile. /// @@ -118,7 +132,4 @@ public class TiffFrameMetadata : IDeepCloneable profile.RemoveValue(ExifTag.Predictor); } } - - /// - public IDeepCloneable DeepClone() => new TiffFrameMetadata(this); } From ede2f2d2d1e567dea74a44a482099302af9ed14d Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:58:40 +0200 Subject: [PATCH 184/220] GifDecoder: Limit lzw bits to a maximum of 12 bits (#2744) * Limit lzw bits to a maximum of 12 bits, fixes issue #2743 * Dispose currentLocalColorTable in final block * Revert "Dispose currentLocalColorTable in final block" This reverts commit 35ea961552dc9a827c6b6119d9ba26b1dd8292fc. * Don't throw; return. --------- Co-authored-by: James Jackson-South --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 11 ++++++--- .../Formats/Gif/GifDecoderTests.cs | 23 +++++++++++-------- tests/ImageSharp.Tests/TestImages.cs | 1 + ...2012BadMinCode_Rgba32_issue2012_drona1.png | 3 +++ .../00.png | 3 +++ .../01.png | 3 +++ .../02.png | 3 +++ .../03.png | 3 +++ .../04.png | 3 +++ .../05.png | 3 +++ .../06.png | 3 +++ .../07.png | 3 +++ tests/Images/Input/Gif/issues/issue_2743.gif | 3 +++ 13 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png create mode 100644 tests/Images/Input/Gif/issues/issue_2743.gif diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 4d282f26c..aecd8e5b4 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -19,6 +19,11 @@ internal sealed class LzwDecoder : IDisposable /// private const int MaxStackSize = 4096; + /// + /// The maximum bits for a lzw code. + /// + private const int MaximumLzwBits = 12; + /// /// The null code. /// @@ -73,12 +78,12 @@ internal sealed class LzwDecoder : IDisposable // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } // The resulting index table length. @@ -245,7 +250,7 @@ internal sealed class LzwDecoder : IDisposable // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 6399e1193..ee108b9d9 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -296,15 +296,9 @@ public class GifDecoderTests public void Issue2012BadMinCode(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Exception ex = Record.Exception( - () => - { - using Image image = provider.GetImage(); - image.DebugSave(provider); - }); - - Assert.NotNull(ex); - Assert.Contains("Gif Image does not contain a valid LZW minimum code.", ex.Message); + using Image image = provider.GetImage(); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 @@ -318,4 +312,15 @@ public class GifDecoderTests image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2743 + [Theory] + [WithFile(TestImages.Gif.Issues.BadMaxLzwBits, PixelTypes.Rgba32)] + public void IssueTooLargeLzwBits(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a45931e29..7aaaac6f8 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -516,6 +516,7 @@ public static class TestImages public const string BadAppExtLength = "Gif/issues/issue405_badappextlength252.gif"; public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif"; public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif"; + public const string BadMaxLzwBits = "Gif/issues/issue_2743.gif"; public const string DeferredClearCode = "Gif/issues/bugzilla-55918.gif"; public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png"; public const string Issue1530 = "Gif/issues/issue1530.gif"; diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png new file mode 100644 index 000000000..cdba9277b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a24c066895fd3a76649da376485cbc1912d6a3ae15369575f523e66364b3b6 +size 141563 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png new file mode 100644 index 000000000..923fbc122 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:800d1ec2d7c7c99d449db1f49ef202cf18214016eae65ebc4216d6f4b1f4d328 +size 537 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png new file mode 100644 index 000000000..6c2134d8b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94dcd97831b16165f3331e429d72d7ef546e04038cab754c7918f9cf535ff30a +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png new file mode 100644 index 000000000..6f50397ea --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png new file mode 100644 index 000000000..82061ba0a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png new file mode 100644 index 000000000..8902eb824 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b78516c9874cb15de4c4b98ed307e8105d962fc6bfa7aa3490b2c7e13b455a2d +size 544 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png new file mode 100644 index 000000000..82061ba0a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png new file mode 100644 index 000000000..6f50397ea --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png new file mode 100644 index 000000000..75cf685e4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:489642f0c81fd12e97007fe6feb11b0e93e351199a922ce038069a3782ad0722 +size 135 diff --git a/tests/Images/Input/Gif/issues/issue_2743.gif b/tests/Images/Input/Gif/issues/issue_2743.gif new file mode 100644 index 000000000..4ce61340d --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2743.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be51cb9c258a6518d791ad2810fa0d71449805a5d5a8f95dcc7da2dc558ed73 +size 166413 From af007fa1cdb0171037e4f36ef72ca7850d610cf6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 14:38:30 +1000 Subject: [PATCH 185/220] Allow skipping bad metadata using identify --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 2 +- .../Formats/Gif/GifMetadataTests.cs | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index aecd8e5b4..19f646b59 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -255,7 +255,7 @@ internal sealed class LzwDecoder : IDisposable // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } int codeSize = minCodeSize + 1; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index fb4445cda..f9c56e05e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -35,7 +35,7 @@ public class GifMetadataTests RepeatCount = 1, ColorTableMode = GifColorTableMode.Global, GlobalColorTable = new[] { Color.Black, Color.White }, - Comments = new List { "Foo" } + Comments = ["Foo"] }; GifMetadata clone = (GifMetadata)meta.DeepClone(); @@ -126,7 +126,7 @@ public class GifMetadataTests public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { TestFile testFile = TestFile.Create(imagePath); - using MemoryStream stream = new(testFile.Bytes, false); + await using MemoryStream stream = new(testFile.Bytes, false); ImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -152,7 +152,7 @@ public class GifMetadataTests public async Task Decode_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { TestFile testFile = TestFile.Create(imagePath); - using MemoryStream stream = new(testFile.Bytes, false); + await using MemoryStream stream = new(testFile.Bytes, false); using Image image = await GifDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -214,4 +214,24 @@ public class GifMetadataTests Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); } + + [Theory] + [InlineData(TestImages.Gif.Issues.BadMaxLzwBits, 8)] + [InlineData(TestImages.Gif.Issues.Issue2012BadMinCode, 1)] + public void Identify_Frames_Bad_Lzw(string imagePath, int framesCount) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + GifMetadata gifMetadata = imageInfo.Metadata.GetGifMetadata(); + Assert.NotNull(gifMetadata); + + Assert.Equal(framesCount, imageInfo.FrameMetadataCollection.Count); + GifFrameMetadata gifFrameMetadata = imageInfo.FrameMetadataCollection[imageInfo.FrameMetadataCollection.Count - 1].GetGifMetadata(); + + Assert.NotNull(gifFrameMetadata); + } } From fbefb37a1042a87315ed13fdaf2a73ee7baf85da Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 17:25:13 +1000 Subject: [PATCH 186/220] Jpeg should not have nullable metadata --- .../Formats/Jpeg/JpegEncoderCore.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 243bbe051..b6fda8bc3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -72,7 +72,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals JpegMetadata jpegMetadata = metadata.GetJpegMetadata(); JpegFrameConfig frameConfig = this.GetFrameConfig(jpegMetadata); - bool interleaved = this.encoder.Interleaved ?? jpegMetadata.Interleaved ?? true; + bool interleaved = this.encoder.Interleaved ?? jpegMetadata.Interleaved; using JpegFrame frame = new(image, frameConfig, interleaved); // Write the Start Of Image marker. diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index d15abafde..86b7883c3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -48,11 +48,12 @@ public class JpegMetadata : IFormatMetadata internal int? ChrominanceQuality { get; set; } /// - /// Gets the encoded quality. + /// Gets or sets the encoded quality. /// /// /// Note that jpeg image can have different quality for luminance and chrominance components. /// This property returns maximum value of luma/chroma qualities if both are present. + /// Setting the quality will update both values. /// public int Quality { @@ -70,34 +71,40 @@ public class JpegMetadata : IFormatMetadata return this.ChrominanceQuality ?? Quantization.DefaultQualityFactor; } + + set + { + this.LuminanceQuality = value; + this.ChrominanceQuality = value; + } } /// - /// Gets the color type. + /// Gets or sets the color type. /// - public JpegEncodingColor? ColorType { get; internal set; } + public JpegEncodingColor ColorType { get; set; } = JpegEncodingColor.YCbCrRatio420; /// - /// Gets the component encoding mode. + /// Gets or sets a value indicating whether the component encoding mode should be interleaved. /// /// /// Interleaved encoding mode encodes all color components in a single scan. /// Non-interleaved encoding mode encodes each color component in a separate scan. /// - public bool? Interleaved { get; internal set; } + public bool Interleaved { get; set; } = true; /// - /// Gets the scan encoding mode. + /// Gets or sets a value indicating whether the scan encoding mode is progressive. /// /// /// Progressive jpeg images encode component data across multiple scans. /// - public bool? Progressive { get; internal set; } + public bool Progressive { get; set; } /// - /// Gets the comments. + /// Gets or sets collection of comments. /// - public IList Comments { get; } + public IList Comments { get; set; } = []; /// public static JpegMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) From 3d80dfcbaca92112dab78c1219207b7bb21fe4c0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 17:27:12 +1000 Subject: [PATCH 187/220] Rename to JpegColorType --- .../EncodingConfigs/JpegFrameConfig.cs | 4 +- .../Components/Encoder/HuffmanScanEncoder.cs | 6 +- ...{JpegEncodingColor.cs => JpegColorType.cs} | 2 +- .../Formats/Jpeg/JpegDecoderCore.cs | 24 ++-- src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 2 +- .../Jpeg/JpegEncoderCore.FrameConfig.cs | 18 +-- .../Formats/Jpeg/JpegEncoderCore.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 24 ++-- .../Compressors/TiffJpegCompressor.cs | 2 +- .../Codecs/Jpeg/EncodeJpegComparison.cs | 2 +- .../Codecs/Jpeg/EncodeJpegFeatures.cs | 12 +- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 28 ++--- .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 10 +- .../Formats/Jpg/JpegEncoderTests.cs | 112 +++++++++--------- .../Formats/Jpg/JpegMetadataTests.cs | 4 +- 15 files changed, 126 insertions(+), 126 deletions(-) rename src/ImageSharp/Formats/Jpeg/{JpegEncodingColor.cs => JpegColorType.cs} (98%) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs index 5a59837e5..0b7b21f90 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs @@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; internal class JpegFrameConfig { - public JpegFrameConfig(JpegColorSpace colorType, JpegEncodingColor encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) + public JpegFrameConfig(JpegColorSpace colorType, JpegColorType encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) { this.ColorType = colorType; this.EncodingColor = encodingColor; @@ -25,7 +25,7 @@ internal class JpegFrameConfig public JpegColorSpace ColorType { get; } - public JpegEncodingColor EncodingColor { get; } + public JpegColorType EncodingColor { get; } public JpegComponentConfig[] Components { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index d74494f9e..ac527ff31 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -139,13 +139,13 @@ internal class HuffmanScanEncoder /// Frame to encode. /// Converter from color to spectral. /// The token to request cancellation. - public void EncodeScanBaselineInterleaved(JpegEncodingColor color, JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) + public void EncodeScanBaselineInterleaved(JpegColorType color, JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { switch (color) { - case JpegEncodingColor.YCbCrRatio444: - case JpegEncodingColor.Rgb: + case JpegColorType.YCbCrRatio444: + case JpegColorType.Rgb: this.EncodeThreeComponentBaselineInterleavedScanNoSubsampling(frame, converter, cancellationToken); break; default: diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncodingColor.cs b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs similarity index 98% rename from src/ImageSharp/Formats/Jpeg/JpegEncodingColor.cs rename to src/ImageSharp/Formats/Jpeg/JpegColorType.cs index 779ccf61e..a8429273f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncodingColor.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Provides enumeration of available JPEG color types. /// -public enum JpegEncodingColor : byte +public enum JpegColorType : byte { /// /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 906505b76..6028aab30 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -603,58 +603,58 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// Returns the jpeg color type based on the colorspace and subsampling used. /// /// Jpeg color type. - private JpegEncodingColor DeduceJpegColorType() + private JpegColorType DeduceJpegColorType() { switch (this.ColorSpace) { case JpegColorSpace.Grayscale: - return JpegEncodingColor.Luminance; + return JpegColorType.Luminance; case JpegColorSpace.RGB: - return JpegEncodingColor.Rgb; + return JpegColorType.Rgb; case JpegColorSpace.YCbCr: if (this.Frame.Components[0].HorizontalSamplingFactor == 1 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio444; + return JpegColorType.YCbCrRatio444; } else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio422; + return JpegColorType.YCbCrRatio422; } else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 2 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio420; + return JpegColorType.YCbCrRatio420; } else if (this.Frame.Components[0].HorizontalSamplingFactor == 4 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio411; + return JpegColorType.YCbCrRatio411; } else if (this.Frame.Components[0].HorizontalSamplingFactor == 4 && this.Frame.Components[0].VerticalSamplingFactor == 2 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) { - return JpegEncodingColor.YCbCrRatio410; + return JpegColorType.YCbCrRatio410; } else { - return JpegEncodingColor.YCbCrRatio420; + return JpegColorType.YCbCrRatio420; } case JpegColorSpace.Cmyk: - return JpegEncodingColor.Cmyk; + return JpegColorType.Cmyk; case JpegColorSpace.Ycck: - return JpegEncodingColor.Ycck; + return JpegColorType.Ycck; default: - return JpegEncodingColor.YCbCrRatio420; + return JpegColorType.YCbCrRatio420; } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 5ff4b1694..0daaae112 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -45,7 +45,7 @@ public sealed class JpegEncoder : ImageEncoder /// /// Gets the jpeg color for encoding. /// - public JpegEncodingColor? ColorType { get; init; } + public JpegColorType? ColorType { get; init; } /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.FrameConfig.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.FrameConfig.cs index 4aed79582..71f852a09 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.FrameConfig.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.FrameConfig.cs @@ -40,7 +40,7 @@ internal sealed unsafe partial class JpegEncoderCore // YCbCr 4:4:4 new JpegFrameConfig( JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio444, + JpegColorType.YCbCrRatio444, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -53,7 +53,7 @@ internal sealed unsafe partial class JpegEncoderCore // YCbCr 4:2:2 new JpegFrameConfig( JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio422, + JpegColorType.YCbCrRatio422, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 2, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -66,7 +66,7 @@ internal sealed unsafe partial class JpegEncoderCore // YCbCr 4:2:0 new JpegFrameConfig( JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio420, + JpegColorType.YCbCrRatio420, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 2, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -79,7 +79,7 @@ internal sealed unsafe partial class JpegEncoderCore // YCbCr 4:1:1 new JpegFrameConfig( JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio411, + JpegColorType.YCbCrRatio411, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 4, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -92,7 +92,7 @@ internal sealed unsafe partial class JpegEncoderCore // YCbCr 4:1:0 new JpegFrameConfig( JpegColorSpace.YCbCr, - JpegEncodingColor.YCbCrRatio410, + JpegColorType.YCbCrRatio410, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 4, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -105,7 +105,7 @@ internal sealed unsafe partial class JpegEncoderCore // Luminance new JpegFrameConfig( JpegColorSpace.Grayscale, - JpegEncodingColor.Luminance, + JpegColorType.Luminance, new JpegComponentConfig[] { new JpegComponentConfig(id: 0, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -123,7 +123,7 @@ internal sealed unsafe partial class JpegEncoderCore // Rgb new JpegFrameConfig( JpegColorSpace.RGB, - JpegEncodingColor.Rgb, + JpegColorType.Rgb, new JpegComponentConfig[] { new JpegComponentConfig(id: 82, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -146,7 +146,7 @@ internal sealed unsafe partial class JpegEncoderCore // Cmyk new JpegFrameConfig( JpegColorSpace.Cmyk, - JpegEncodingColor.Cmyk, + JpegColorType.Cmyk, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), @@ -170,7 +170,7 @@ internal sealed unsafe partial class JpegEncoderCore // YccK new JpegFrameConfig( JpegColorSpace.Ycck, - JpegEncodingColor.Ycck, + JpegColorType.Ycck, new JpegComponentConfig[] { new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index b6fda8bc3..523d6b3ba 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -780,7 +780,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals private JpegFrameConfig GetFrameConfig(JpegMetadata metadata) { - JpegEncodingColor color = this.encoder.ColorType ?? metadata.ColorType ?? JpegEncodingColor.YCbCrRatio420; + JpegColorType color = this.encoder.ColorType ?? metadata.ColorType; JpegFrameConfig frameConfig = Array.Find( FrameConfigs, cfg => cfg.EncodingColor == color); diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 86b7883c3..0fadc830e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -82,7 +82,7 @@ public class JpegMetadata : IFormatMetadata /// /// Gets or sets the color type. /// - public JpegEncodingColor ColorType { get; set; } = JpegEncodingColor.YCbCrRatio420; + public JpegColorType ColorType { get; set; } = JpegColorType.YCbCrRatio420; /// /// Gets or sets a value indicating whether the component encoding mode should be interleaved. @@ -109,29 +109,29 @@ public class JpegMetadata : IFormatMetadata /// public static JpegMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { - JpegEncodingColor color; + JpegColorType color; PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.YCbCr; switch (colorType) { case PixelColorType.Luminance: - color = JpegEncodingColor.Luminance; + color = JpegColorType.Luminance; break; case PixelColorType.CMYK: - color = JpegEncodingColor.Cmyk; + color = JpegColorType.Cmyk; break; case PixelColorType.YCCK: - color = JpegEncodingColor.Ycck; + color = JpegColorType.Ycck; break; default: if (colorType.HasFlag(PixelColorType.RGB) || colorType.HasFlag(PixelColorType.BGR)) { - color = JpegEncodingColor.Rgb; + color = JpegColorType.Rgb; } else { color = metadata.Quality <= Quantization.DefaultQualityFactor - ? JpegEncodingColor.YCbCrRatio420 - : JpegEncodingColor.YCbCrRatio444; + ? JpegColorType.YCbCrRatio420 + : JpegColorType.YCbCrRatio444; } break; @@ -153,22 +153,22 @@ public class JpegMetadata : IFormatMetadata PixelComponentInfo info; switch (this.ColorType) { - case JpegEncodingColor.Luminance: + case JpegColorType.Luminance: bpp = 8; colorType = PixelColorType.Luminance; info = PixelComponentInfo.Create(1, bpp, 8); break; - case JpegEncodingColor.Cmyk: + case JpegColorType.Cmyk: bpp = 32; colorType = PixelColorType.CMYK; info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); break; - case JpegEncodingColor.Ycck: + case JpegColorType.Ycck: bpp = 32; colorType = PixelColorType.YCCK; info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); break; - case JpegEncodingColor.Rgb: + case JpegColorType.Rgb: bpp = 24; colorType = PixelColorType.RGB; info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs index 9096271fe..08faa539a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs @@ -33,7 +33,7 @@ internal class TiffJpegCompressor : TiffBaseCompressor var image = Image.LoadPixelData(rows, width, height); image.Save(memoryStream, new JpegEncoder() { - ColorType = JpegEncodingColor.Rgb + ColorType = JpegColorType.Rgb }); memoryStream.Position = 0; memoryStream.WriteTo(this.Output); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs index d762e8e95..deb3125b3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs @@ -39,7 +39,7 @@ public class EncodeJpegComparison using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); this.imageImageSharp = Image.Load(imageBinaryStream); - this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegEncodingColor.YCbCrRatio420 }; + this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 }; this.destinationStream = new MemoryStream(); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs index 98eb0b54d..0692c5a3b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs @@ -20,19 +20,19 @@ public class EncodeJpegFeatures // No metadata private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - public static IEnumerable ColorSpaceValues => new[] + public static IEnumerable ColorSpaceValues => new[] { - JpegEncodingColor.Luminance, - JpegEncodingColor.Rgb, - JpegEncodingColor.YCbCrRatio420, - JpegEncodingColor.YCbCrRatio444, + JpegColorType.Luminance, + JpegColorType.Rgb, + JpegColorType.YCbCrRatio420, + JpegColorType.YCbCrRatio444, }; [Params(75, 90, 100)] public int Quality; [ParamsSource(nameof(ColorSpaceValues), Priority = -100)] - public JpegEncodingColor TargetColorSpace; + public JpegColorType TargetColorSpace; private Image bmpCore; private JpegEncoder encoder; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index cbb2befcd..a2c144a65 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -145,14 +145,14 @@ public partial class JpegDecoderTests } [Theory] - [InlineData(TestImages.Jpeg.Baseline.Floorplan, JpegEncodingColor.Luminance)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, JpegEncodingColor.YCbCrRatio420)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg444, JpegEncodingColor.YCbCrRatio444)] - [InlineData(TestImages.Jpeg.Baseline.JpegRgb, JpegEncodingColor.Rgb)] - [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegEncodingColor.Cmyk)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg410, JpegEncodingColor.YCbCrRatio410)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg411, JpegEncodingColor.YCbCrRatio411)] - public void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor expectedColorType) + [InlineData(TestImages.Jpeg.Baseline.Floorplan, JpegColorType.Luminance)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, JpegColorType.YCbCrRatio420)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg444, JpegColorType.YCbCrRatio444)] + [InlineData(TestImages.Jpeg.Baseline.JpegRgb, JpegColorType.Rgb)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorType.Cmyk)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg410, JpegColorType.YCbCrRatio410)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg411, JpegColorType.YCbCrRatio411)] + public void Identify_DetectsCorrectColorType(string imagePath, JpegColorType expectedColorType) { TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); @@ -162,12 +162,12 @@ public partial class JpegDecoderTests } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegEncodingColor.Luminance)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] - [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegEncodingColor.Rgb)] - [WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgb24, JpegEncodingColor.Cmyk)] - public void Decode_DetectsCorrectColorType(TestImageProvider provider, JpegEncodingColor expectedColorType) + [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegColorType.Luminance)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegColorType.YCbCrRatio420)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegColorType.YCbCrRatio444)] + [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegColorType.Rgb)] + [WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgb24, JpegColorType.Cmyk)] + public void Decode_DetectsCorrectColorType(TestImageProvider provider, JpegColorType expectedColorType) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(JpegDecoder.Instance); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index f06fbe963..039215bbc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -208,11 +208,11 @@ public partial class JpegEncoderTests } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegEncodingColor.Luminance)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)] - [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegEncodingColor.Rgb)] - public void Encode_PreservesColorType(TestImageProvider provider, JpegEncodingColor expectedColorType) + [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegColorType.Luminance)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegColorType.YCbCrRatio444)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegColorType.YCbCrRatio420)] + [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegColorType.Rgb)] + public void Encode_PreservesColorType(TestImageProvider provider, JpegColorType expectedColorType) where TPixel : unmanaged, IPixel { // arrange diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 5842c8e1a..1f4b3e465 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -21,51 +21,51 @@ public partial class JpegEncoderTests 100, }; - public static readonly TheoryData NonSubsampledEncodingSetups = new() + public static readonly TheoryData NonSubsampledEncodingSetups = new() { - { JpegEncodingColor.Rgb, 100, 0.0238f / 100 }, - { JpegEncodingColor.Rgb, 80, 1.3044f / 100 }, - { JpegEncodingColor.Rgb, 40, 2.9879f / 100 }, - { JpegEncodingColor.YCbCrRatio444, 100, 0.0780f / 100 }, - { JpegEncodingColor.YCbCrRatio444, 80, 1.4585f / 100 }, - { JpegEncodingColor.YCbCrRatio444, 40, 3.1413f / 100 }, + { JpegColorType.Rgb, 100, 0.0238f / 100 }, + { JpegColorType.Rgb, 80, 1.3044f / 100 }, + { JpegColorType.Rgb, 40, 2.9879f / 100 }, + { JpegColorType.YCbCrRatio444, 100, 0.0780f / 100 }, + { JpegColorType.YCbCrRatio444, 80, 1.4585f / 100 }, + { JpegColorType.YCbCrRatio444, 40, 3.1413f / 100 }, }; - public static readonly TheoryData SubsampledEncodingSetups = new() + public static readonly TheoryData SubsampledEncodingSetups = new() { - { JpegEncodingColor.YCbCrRatio422, 100, 0.4895f / 100 }, - { JpegEncodingColor.YCbCrRatio422, 80, 1.6043f / 100 }, - { JpegEncodingColor.YCbCrRatio422, 40, 3.1996f / 100 }, - { JpegEncodingColor.YCbCrRatio420, 100, 0.5790f / 100 }, - { JpegEncodingColor.YCbCrRatio420, 80, 1.6692f / 100 }, - { JpegEncodingColor.YCbCrRatio420, 40, 3.2324f / 100 }, - { JpegEncodingColor.YCbCrRatio411, 100, 0.6868f / 100 }, - { JpegEncodingColor.YCbCrRatio411, 80, 1.7139f / 100 }, - { JpegEncodingColor.YCbCrRatio411, 40, 3.2634f / 100 }, - { JpegEncodingColor.YCbCrRatio410, 100, 0.7357f / 100 }, - { JpegEncodingColor.YCbCrRatio410, 80, 1.7495f / 100 }, - { JpegEncodingColor.YCbCrRatio410, 40, 3.2911f / 100 }, + { JpegColorType.YCbCrRatio422, 100, 0.4895f / 100 }, + { JpegColorType.YCbCrRatio422, 80, 1.6043f / 100 }, + { JpegColorType.YCbCrRatio422, 40, 3.1996f / 100 }, + { JpegColorType.YCbCrRatio420, 100, 0.5790f / 100 }, + { JpegColorType.YCbCrRatio420, 80, 1.6692f / 100 }, + { JpegColorType.YCbCrRatio420, 40, 3.2324f / 100 }, + { JpegColorType.YCbCrRatio411, 100, 0.6868f / 100 }, + { JpegColorType.YCbCrRatio411, 80, 1.7139f / 100 }, + { JpegColorType.YCbCrRatio411, 40, 3.2634f / 100 }, + { JpegColorType.YCbCrRatio410, 100, 0.7357f / 100 }, + { JpegColorType.YCbCrRatio410, 80, 1.7495f / 100 }, + { JpegColorType.YCbCrRatio410, 40, 3.2911f / 100 }, }; - public static readonly TheoryData CmykEncodingSetups = new() + public static readonly TheoryData CmykEncodingSetups = new() { - { JpegEncodingColor.Cmyk, 100, 0.0159f / 100 }, - { JpegEncodingColor.Cmyk, 80, 0.3922f / 100 }, - { JpegEncodingColor.Cmyk, 40, 0.6488f / 100 }, + { JpegColorType.Cmyk, 100, 0.0159f / 100 }, + { JpegColorType.Cmyk, 80, 0.3922f / 100 }, + { JpegColorType.Cmyk, 40, 0.6488f / 100 }, }; - public static readonly TheoryData YcckEncodingSetups = new() + public static readonly TheoryData YcckEncodingSetups = new() { - { JpegEncodingColor.Ycck, 100, 0.0356f / 100 }, - { JpegEncodingColor.Ycck, 80, 0.1245f / 100 }, - { JpegEncodingColor.Ycck, 40, 0.2663f / 100 }, + { JpegColorType.Ycck, 100, 0.0356f / 100 }, + { JpegColorType.Ycck, 80, 0.1245f / 100 }, + { JpegColorType.Ycck, 40, 0.2663f / 100 }, }; - public static readonly TheoryData LuminanceEncodingSetups = new() + public static readonly TheoryData LuminanceEncodingSetups = new() { - { JpegEncodingColor.Luminance, 100, 0.0175f / 100 }, - { JpegEncodingColor.Luminance, 80, 0.6730f / 100 }, - { JpegEncodingColor.Luminance, 40, 0.9943f / 100 }, + { JpegColorType.Luminance, 100, 0.0175f / 100 }, + { JpegColorType.Luminance, 80, 0.6730f / 100 }, + { JpegColorType.Luminance, 40, 0.9943f / 100 }, }; [Theory] @@ -74,7 +74,7 @@ public partial class JpegEncoderTests [WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)] [WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)] [WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)] - public void EncodeBaseline_Interleaved(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_Interleaved(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, tolerance); [Theory] @@ -83,7 +83,7 @@ public partial class JpegEncoderTests [WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)] [WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)] [WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)] - public void EncodeBaseline_NonInterleavedMode(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_NonInterleavedMode(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); @@ -108,7 +108,7 @@ public partial class JpegEncoderTests [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 153, 21, PixelTypes.Rgb24)] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 143, 81, PixelTypes.Rgb24)] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 138, 24, PixelTypes.Rgb24)] - public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality); [Theory] @@ -121,7 +121,7 @@ public partial class JpegEncoderTests [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 24, PixelTypes.Rgb24)] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 46, 8, PixelTypes.Rgb24)] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 51, 7, PixelTypes.Rgb24)] - public void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, ImageComparer.Tolerant(0.12f)); [Theory] @@ -131,28 +131,28 @@ public partial class JpegEncoderTests [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)] public void EncodeBaseline_Grayscale(TestImageProvider provider, int quality) - where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, JpegEncodingColor.Luminance, quality); + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, JpegColorType.Luminance, quality); [Theory] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 96, 96, PixelTypes.Rgb24 | PixelTypes.Bgr24)] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 48, PixelTypes.Rgb24 | PixelTypes.Bgr24)] - public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality); [Theory] [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 48, PixelTypes.Rgb24 | PixelTypes.Bgr24)] - public void EncodeBaseline_WithSmallImages_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + public void EncodeBaseline_WithSmallImages_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, comparer: ImageComparer.Tolerant(0.06f)); [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] - [WithTestPatternImages(587, 821, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)] - [WithTestPatternImages(677, 683, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)] - [WithSolidFilledImages(400, 400, nameof(Color.Red), PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)] - public void EncodeBaseline_WorksWithDiscontiguousBuffers(TestImageProvider provider, JpegEncodingColor colorType) + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgb24, JpegColorType.YCbCrRatio444)] + [WithTestPatternImages(587, 821, PixelTypes.Rgb24, JpegColorType.YCbCrRatio444)] + [WithTestPatternImages(677, 683, PixelTypes.Rgb24, JpegColorType.YCbCrRatio420)] + [WithSolidFilledImages(400, 400, nameof(Color.Red), PixelTypes.Rgb24, JpegColorType.YCbCrRatio420)] + public void EncodeBaseline_WorksWithDiscontiguousBuffers(TestImageProvider provider, JpegColorType colorType) where TPixel : unmanaged, IPixel { - ImageComparer comparer = colorType == JpegEncodingColor.YCbCrRatio444 + ImageComparer comparer = colorType == JpegColorType.YCbCrRatio444 ? ImageComparer.TolerantPercentage(0.1f) : ImageComparer.TolerantPercentage(5f); @@ -161,9 +161,9 @@ public partial class JpegEncoderTests } [Theory] - [InlineData(JpegEncodingColor.YCbCrRatio420)] - [InlineData(JpegEncodingColor.YCbCrRatio444)] - public async Task Encode_IsCancellable(JpegEncodingColor colorType) + [InlineData(JpegColorType.YCbCrRatio420)] + [InlineData(JpegColorType.YCbCrRatio444)] + public async Task Encode_IsCancellable(JpegColorType colorType) { CancellationTokenSource cts = new(); using PausedStream pausedStream = new(new MemoryStream()); @@ -201,13 +201,13 @@ public partial class JpegEncoderTests image.Mutate(x => x.Crop(132, 1606)); int[] quality = new int[] { 100, 50 }; - JpegEncodingColor[] colors = new[] { JpegEncodingColor.YCbCrRatio444, JpegEncodingColor.YCbCrRatio420 }; + JpegColorType[] colors = new[] { JpegColorType.YCbCrRatio444, JpegColorType.YCbCrRatio420 }; for (int i = 0; i < quality.Length; i++) { int q = quality[i]; for (int j = 0; j < colors.Length; j++) { - JpegEncodingColor c = colors[j]; + JpegColorType c = colors[j]; image.VerifyEncoder(provider, "jpeg", $"{q}-{c}", new JpegEncoder() { Quality = q, ColorType = c }, GetComparer(q, c)); } } @@ -216,7 +216,7 @@ public partial class JpegEncoderTests /// /// Anton's SUPER-SCIENTIFIC tolerance threshold calculation /// - private static ImageComparer GetComparer(int quality, JpegEncodingColor? colorType) + private static ImageComparer GetComparer(int quality, JpegColorType? colorType) { float tolerance = 0.015f; // ~1.5% @@ -224,10 +224,10 @@ public partial class JpegEncoderTests { tolerance *= 4.5f; } - else if (quality < 75 || colorType == JpegEncodingColor.YCbCrRatio420) + else if (quality < 75 || colorType == JpegColorType.YCbCrRatio420) { tolerance *= 2.0f; - if (colorType == JpegEncodingColor.YCbCrRatio420) + if (colorType == JpegColorType.YCbCrRatio420) { tolerance *= 2.0f; } @@ -236,15 +236,15 @@ public partial class JpegEncoderTests return ImageComparer.Tolerant(tolerance); } - private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality) + private static void TestJpegEncoderCore(TestImageProvider provider, JpegColorType colorType, int quality) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, GetComparer(quality, colorType)); - private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance) + private static void TestJpegEncoderCore(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, new TolerantImageComparer(tolerance)); - private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality, ImageComparer comparer) + private static void TestJpegEncoderCore(TestImageProvider provider, JpegColorType colorType, int quality, ImageComparer comparer) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index e07c42f89..19b5265a1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -12,10 +12,10 @@ public class JpegMetadataTests [Fact] public void CloneIsDeep() { - var meta = new JpegMetadata { ColorType = JpegEncodingColor.Luminance }; + var meta = new JpegMetadata { ColorType = JpegColorType.Luminance }; var clone = (JpegMetadata)meta.DeepClone(); - clone.ColorType = JpegEncodingColor.YCbCrRatio420; + clone.ColorType = JpegColorType.YCbCrRatio420; Assert.False(meta.ColorType.Equals(clone.ColorType)); } From 0e5408123287fc955536eae56af9dedefdcbba1d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 22:48:11 +1000 Subject: [PATCH 188/220] Fix webp bpp parsing, normalize property naming. --- src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs | 14 +-- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 24 ++--- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 30 +++---- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 4 +- src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs | 8 +- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 2 +- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 22 ++--- src/ImageSharp/Formats/Tga/TgaMetadata.cs | 18 ++-- .../Formats/Webp/WebpBitsPerPixel.cs | 4 +- .../Formats/Webp/WebpChunkParsingUtils.cs | 37 +++++--- src/ImageSharp/Formats/Webp/WebpColorType.cs | 25 ++++++ .../Formats/Webp/WebpDecoderCore.cs | 21 +++-- src/ImageSharp/Formats/Webp/WebpImageInfo.cs | 6 ++ src/ImageSharp/Formats/Webp/WebpMetadata.cs | 33 +++++-- .../Formats/Bmp/BmpEncoderTests.cs | 90 +++++++++---------- .../Formats/Bmp/BmpMetadataTests.cs | 4 +- .../Formats/Tga/TgaEncoderTests.cs | 32 +++---- .../Formats/Tga/TgaFileHeaderTests.cs | 8 +- .../Formats/WebP/WebpDecoderTests.cs | 4 +- 19 files changed, 229 insertions(+), 157 deletions(-) create mode 100644 src/ImageSharp/Formats/Webp/WebpColorType.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 5700bb444..3c9e3ce79 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -11,35 +11,35 @@ public enum BmpBitsPerPixel : short /// /// 1 bit per pixel. /// - Pixel1 = 1, + Bit1 = 1, /// /// 2 bits per pixel. /// - Pixel2 = 2, + Bit2 = 2, /// /// 4 bits per pixel. /// - Pixel4 = 4, + Bit4 = 4, /// /// 8 bits per pixel. Each pixel consists of 1 byte. /// - Pixel8 = 8, + Bit8 = 8, /// /// 16 bits per pixel. Each pixel consists of 2 bytes. /// - Pixel16 = 16, + Bit16 = 16, /// /// 24 bits per pixel. Each pixel consists of 3 bytes. /// - Pixel24 = 24, + Bit24 = 24, /// /// 32 bits per pixel. Each pixel consists of 4 bytes. /// - Pixel32 = 32 + Bit32 = 32 } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 076d1adf0..24a4fa2f5 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -130,10 +130,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals int colorPaletteSize = this.bitsPerPixel switch { - BmpBitsPerPixel.Pixel8 => ColorPaletteSize8Bit, - BmpBitsPerPixel.Pixel4 => ColorPaletteSize4Bit, - BmpBitsPerPixel.Pixel2 => ColorPaletteSize2Bit, - BmpBitsPerPixel.Pixel1 => ColorPaletteSize1Bit, + BmpBitsPerPixel.Bit8 => ColorPaletteSize8Bit, + BmpBitsPerPixel.Bit4 => ColorPaletteSize4Bit, + BmpBitsPerPixel.Bit2 => ColorPaletteSize2Bit, + BmpBitsPerPixel.Bit1 => ColorPaletteSize1Bit, _ => 0 }; @@ -220,7 +220,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals clrUsed: 0, clrImportant: 0); - if ((this.infoHeaderType is BmpInfoHeaderType.WinVersion4 or BmpInfoHeaderType.WinVersion5) && this.bitsPerPixel == BmpBitsPerPixel.Pixel32) + if ((this.infoHeaderType is BmpInfoHeaderType.WinVersion4 or BmpInfoHeaderType.WinVersion5) && this.bitsPerPixel == BmpBitsPerPixel.Bit32) { infoHeader.AlphaMask = Rgba32AlphaMask; infoHeader.RedMask = Rgba32RedMask; @@ -319,31 +319,31 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Buffer2D pixels = image.Frames.RootFrame.PixelBuffer; switch (this.bitsPerPixel) { - case BmpBitsPerPixel.Pixel32: + case BmpBitsPerPixel.Bit32: this.Write32BitPixelData(configuration, stream, pixels); break; - case BmpBitsPerPixel.Pixel24: + case BmpBitsPerPixel.Bit24: this.Write24BitPixelData(configuration, stream, pixels); break; - case BmpBitsPerPixel.Pixel16: + case BmpBitsPerPixel.Bit16: this.Write16BitPixelData(configuration, stream, pixels); break; - case BmpBitsPerPixel.Pixel8: + case BmpBitsPerPixel.Bit8: this.Write8BitPixelData(configuration, stream, image); break; - case BmpBitsPerPixel.Pixel4: + case BmpBitsPerPixel.Bit4: this.Write4BitPixelData(configuration, stream, image); break; - case BmpBitsPerPixel.Pixel2: + case BmpBitsPerPixel.Bit2: this.Write2BitPixelData(configuration, stream, image); break; - case BmpBitsPerPixel.Pixel1: + case BmpBitsPerPixel.Bit1: this.Write1BitPixelData(configuration, stream, image); break; } diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index d44520a4f..bad47bd0d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -36,7 +36,7 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata /// Gets or sets the number of bits per pixel. /// - public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Bit24; /// public static BmpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) @@ -44,23 +44,23 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel1 }, - 2 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel2 }, - <= 4 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel4 }, - <= 8 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }, + 1 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Bit1 }, + 2 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Bit2 }, + <= 4 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Bit4 }, + <= 8 => new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Bit8 }, <= 16 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel16, + BitsPerPixel = BmpBitsPerPixel.Bit16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 }, <= 24 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel24, + BitsPerPixel = BmpBitsPerPixel.Bit24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 }, _ => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel32, + BitsPerPixel = BmpBitsPerPixel.Bit32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 } }; @@ -92,34 +92,34 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata /// /// Initializes a new instance of the class. /// - public JpegMetadata() => this.Comments = []; + public JpegMetadata() + { + } /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs index da34e62f7..af537ddc2 100644 --- a/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs @@ -11,20 +11,20 @@ public enum TgaBitsPerPixel : byte /// /// 8 bits per pixel. Each pixel consists of 1 byte. /// - Pixel8 = 8, + Bit8 = 8, /// /// 16 bits per pixel. Each pixel consists of 2 bytes. /// - Pixel16 = 16, + Bit16 = 16, /// /// 24 bits per pixel. Each pixel consists of 3 bytes. /// - Pixel24 = 24, + Bit24 = 24, /// /// 32 bits per pixel. Each pixel consists of 4 bytes. /// - Pixel32 = 32 + Bit32 = 32 } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index b454c677b..47f7eb0f2 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -934,7 +934,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); } - private bool IsTrueColor32BitPerPixel(TgaBitsPerPixel bitsPerPixel) => bitsPerPixel == TgaBitsPerPixel.Pixel32 && + private bool IsTrueColor32BitPerPixel(TgaBitsPerPixel bitsPerPixel) => bitsPerPixel == TgaBitsPerPixel.Bit32 && (this.fileHeader.ImageType == TgaImageType.TrueColor || this.fileHeader.ImageType == TgaImageType.RleTrueColor); } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 14351ee67..8e1309656 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -59,7 +59,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals this.bitsPerPixel ??= tgaMetadata.BitsPerPixel; TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor; - if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8) + if (this.bitsPerPixel == TgaBitsPerPixel.Bit8) { imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite; } @@ -71,13 +71,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals imageDescriptor |= 0x20; } - if (this.bitsPerPixel is TgaBitsPerPixel.Pixel32) + if (this.bitsPerPixel is TgaBitsPerPixel.Bit32) { // Indicate, that 8 bit are used for the alpha channel. imageDescriptor |= 0x8; } - if (this.bitsPerPixel is TgaBitsPerPixel.Pixel16) + if (this.bitsPerPixel is TgaBitsPerPixel.Bit16) { // Indicate, that 1 bit is used for the alpha channel. imageDescriptor |= 0x1; @@ -130,19 +130,19 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals Buffer2D pixels = image.PixelBuffer; switch (this.bitsPerPixel) { - case TgaBitsPerPixel.Pixel8: + case TgaBitsPerPixel.Bit8: this.Write8Bit(configuration, stream, pixels); break; - case TgaBitsPerPixel.Pixel16: + case TgaBitsPerPixel.Bit16: this.Write16Bit(configuration, stream, pixels); break; - case TgaBitsPerPixel.Pixel24: + case TgaBitsPerPixel.Bit24: this.Write24Bit(configuration, stream, pixels); break; - case TgaBitsPerPixel.Pixel32: + case TgaBitsPerPixel.Bit32: this.Write32Bit(configuration, stream, pixels); break; } @@ -208,12 +208,12 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { switch (this.bitsPerPixel) { - case TgaBitsPerPixel.Pixel8: + case TgaBitsPerPixel.Bit8: L8 l8 = L8.FromRgba32(color); stream.WriteByte(l8.PackedValue); break; - case TgaBitsPerPixel.Pixel16: + case TgaBitsPerPixel.Bit16: Bgra5551 bgra5551 = Bgra5551.FromRgba32(color); Span buffer = stackalloc byte[2]; BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)bgra5551.PackedValue); @@ -222,13 +222,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals break; - case TgaBitsPerPixel.Pixel24: + case TgaBitsPerPixel.Bit24: stream.WriteByte(color.B); stream.WriteByte(color.G); stream.WriteByte(color.R); break; - case TgaBitsPerPixel.Pixel32: + case TgaBitsPerPixel.Bit32: stream.WriteByte(color.B); stream.WriteByte(color.G); stream.WriteByte(color.R); diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs index fa8e8b6f4..58b511952 100644 --- a/src/ImageSharp/Formats/Tga/TgaMetadata.cs +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -27,7 +27,7 @@ public class TgaMetadata : IFormatMetadata /// /// Gets or sets the number of bits per pixel. /// - public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24; + public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Bit24; /// /// Gets or sets the number of alpha bits per pixel. @@ -41,10 +41,10 @@ public class TgaMetadata : IFormatMetadata int bpp = metadata.PixelTypeInfo.BitsPerPixel; return bpp switch { - <= 8 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel8 }, - <= 16 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel16 }, - <= 24 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel24 }, - _ => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel32 } + <= 8 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Bit8 }, + <= 16 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Bit16 }, + <= 24 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Bit24 }, + _ => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Bit32 } }; } @@ -57,22 +57,22 @@ public class TgaMetadata : IFormatMetadata PixelAlphaRepresentation alpha; switch (this.BitsPerPixel) { - case TgaBitsPerPixel.Pixel8: + case TgaBitsPerPixel.Bit8: info = PixelComponentInfo.Create(1, bpp, 8); color = PixelColorType.Luminance; alpha = PixelAlphaRepresentation.None; break; - case TgaBitsPerPixel.Pixel16: + case TgaBitsPerPixel.Bit16: info = PixelComponentInfo.Create(1, bpp, 5, 5, 5, 1); color = PixelColorType.BGR | PixelColorType.Alpha; alpha = PixelAlphaRepresentation.Unassociated; break; - case TgaBitsPerPixel.Pixel24: + case TgaBitsPerPixel.Bit24: info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); color = PixelColorType.RGB; alpha = PixelAlphaRepresentation.None; break; - case TgaBitsPerPixel.Pixel32 or _: + case TgaBitsPerPixel.Bit32 or _: info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); color = PixelColorType.RGB | PixelColorType.Alpha; alpha = PixelAlphaRepresentation.Unassociated; diff --git a/src/ImageSharp/Formats/Webp/WebpBitsPerPixel.cs b/src/ImageSharp/Formats/Webp/WebpBitsPerPixel.cs index 529c4bafb..03717d852 100644 --- a/src/ImageSharp/Formats/Webp/WebpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Webp/WebpBitsPerPixel.cs @@ -11,10 +11,10 @@ public enum WebpBitsPerPixel : short /// /// 24 bits per pixel. Each pixel consists of 3 bytes. /// - Pixel24 = 24, + Bit24 = 24, /// /// 32 bits per pixel. Each pixel consists of 4 bytes (an alpha channel is present). /// - Pixel32 = 32 + Bit32 = 32 } diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index 07f09d45e..4ccaf6503 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -17,6 +17,10 @@ internal static class WebpChunkParsingUtils /// /// Reads the header of a lossy webp image. /// + /// The memory allocator. + /// The buffered read stream. + /// The scratch buffer to use while reading. + /// The webp features to parse. /// Information about this webp image. public static WebpImageInfo ReadVp8Header(MemoryAllocator memoryAllocator, BufferedReadStream stream, Span buffer, WebpFeatures features) { @@ -114,13 +118,15 @@ internal static class WebpChunkParsingUtils Vp8BitReader bitReader = new(stream, remaining, memoryAllocator, partitionLength) { Remaining = remaining }; - return new WebpImageInfo + return new() { Width = width, Height = height, XScale = xScale, YScale = yScale, - BitsPerPixel = features?.Alpha == true ? WebpBitsPerPixel.Pixel32 : WebpBitsPerPixel.Pixel24, + + // Vp8 header can be parsed during the processing of the Vp8X header. + BitsPerPixel = features?.Alpha == true ? WebpBitsPerPixel.Bit32 : WebpBitsPerPixel.Bit24, IsLossless = false, Features = features, Vp8Profile = (sbyte)version, @@ -132,7 +138,10 @@ internal static class WebpChunkParsingUtils /// /// Reads the header of a lossless webp image. /// - /// Information about this image. + /// The memory allocator. + /// The buffered read stream. + /// The scratch buffer to use while reading. + /// The webp features to parse. public static WebpImageInfo ReadVp8LHeader(MemoryAllocator memoryAllocator, BufferedReadStream stream, Span buffer, WebpFeatures features) { // VP8 data size. @@ -156,8 +165,8 @@ internal static class WebpChunkParsingUtils } // The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise. - // TODO: this flag value is not used yet - bool alphaIsUsed = bitReader.ReadBit(); + // Alpha may have already been set by the VP8X chunk. + features.Alpha |= bitReader.ReadBit(); // The next 3 bits are the version. The version number is a 3 bit code that must be set to 0. // Any other value should be treated as an error. @@ -167,11 +176,11 @@ internal static class WebpChunkParsingUtils WebpThrowHelper.ThrowNotSupportedException($"Unexpected version number {version} found in VP8L header"); } - return new WebpImageInfo + return new() { Width = width, Height = height, - BitsPerPixel = WebpBitsPerPixel.Pixel32, + BitsPerPixel = features.Alpha ? WebpBitsPerPixel.Bit32 : WebpBitsPerPixel.Bit24, IsLossless = true, Features = features, Vp8LBitReader = bitReader @@ -187,6 +196,9 @@ internal static class WebpChunkParsingUtils /// - An optional 'ALPH' chunk with alpha channel data. /// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow. /// + /// The buffered read stream. + /// The scratch buffer to use while reading. + /// The webp features to parse. /// Information about this webp image. public static WebpImageInfo ReadVp8XHeader(BufferedReadStream stream, Span buffer, WebpFeatures features) { @@ -217,11 +229,8 @@ internal static class WebpChunkParsingUtils features.Animation = (imageFeatures & (1 << 1)) != 0; // 3 reserved bytes should follow which are supposed to be zero. + // No other decoder actually checks this though. stream.Read(buffer, 0, 3); - if (buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0) - { - WebpThrowHelper.ThrowImageFormatException("reserved bytes should be zero"); - } // 3 bytes for the width. uint width = ReadUInt24LittleEndian(stream, buffer) + 1; @@ -230,14 +239,14 @@ internal static class WebpChunkParsingUtils uint height = ReadUInt24LittleEndian(stream, buffer) + 1; // Read all the chunks in the order they occur. - WebpImageInfo info = new() + return new() { Width = width, Height = height, Features = features - }; - return info; + // Additional properties are set during the parsing of the VP8 or VP8L headers. + }; } /// diff --git a/src/ImageSharp/Formats/Webp/WebpColorType.cs b/src/ImageSharp/Formats/Webp/WebpColorType.cs new file mode 100644 index 000000000..64d9143b1 --- /dev/null +++ b/src/ImageSharp/Formats/Webp/WebpColorType.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Webp; + +/// +/// Provides enumeration of the various webp color types. +/// +public enum WebpColorType +{ + /// + /// Yuv (luminance, blue chroma, red chroma) as defined in the ITU-R Rec. BT.709 specification. + /// + Yuv, + + /// + /// Rgb color space. + /// + Rgb, + + /// + /// Rgba color space. + /// + Rgba +} diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 21f0f4946..c29742da5 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -186,36 +186,43 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable Span buffer = stackalloc byte[4]; WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); + WebpImageInfo? info = null; WebpFeatures features = new(); switch (chunkType) { case WebpChunkType.Vp8: + info = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); webpMetadata.FileFormat = WebpFileFormatType.Lossy; - return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); + webpMetadata.ColorType = WebpColorType.Yuv; + return info; case WebpChunkType.Vp8L: + info = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); webpMetadata.FileFormat = WebpFileFormatType.Lossless; - return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); + webpMetadata.ColorType = info.Features?.Alpha == true ? WebpColorType.Rgba : WebpColorType.Rgb; + return info; case WebpChunkType.Vp8X: - WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(stream, buffer, features); + info = WebpChunkParsingUtils.ReadVp8XHeader(stream, buffer, features); while (stream.Position < stream.Length) { chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); if (chunkType == WebpChunkType.Vp8) { + info = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); webpMetadata.FileFormat = WebpFileFormatType.Lossy; - webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); + webpMetadata.ColorType = info.Features?.Alpha == true ? WebpColorType.Rgba : WebpColorType.Rgb; } else if (chunkType == WebpChunkType.Vp8L) { + info = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); webpMetadata.FileFormat = WebpFileFormatType.Lossless; - webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); + webpMetadata.ColorType = info.Features?.Alpha == true ? WebpColorType.Rgba : WebpColorType.Rgb; } else if (WebpChunkParsingUtils.IsOptionalVp8XChunk(chunkType)) { bool isAnimationChunk = this.ParseOptionalExtendedChunks(stream, metadata, chunkType, features, ignoreAlpha, buffer); if (isAnimationChunk) { - return webpInfos; + return info; } } else @@ -226,7 +233,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable } } - return webpInfos; + return info; default: WebpThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); return diff --git a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs index 5f7301b26..3428ce199 100644 --- a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs +++ b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs @@ -18,8 +18,14 @@ internal class WebpImageInfo : IDisposable /// public uint Height { get; set; } + /// + /// Gets or sets the horizontal scale. + /// public sbyte XScale { get; set; } + /// + /// Gets or sets the vertical scale. + /// public sbyte YScale { get; set; } /// diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs index 536ea0929..d8cd29d14 100644 --- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Provides Webp specific metadata information for the image. /// -public class WebpMetadata : IDeepCloneable +public class WebpMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -21,15 +21,27 @@ public class WebpMetadata : IDeepCloneable /// The metadata to create an instance from. private WebpMetadata(WebpMetadata other) { + this.BitsPerPixel = other.BitsPerPixel; + this.ColorType = other.ColorType; this.FileFormat = other.FileFormat; this.RepeatCount = other.RepeatCount; this.BackgroundColor = other.BackgroundColor; } + /// + /// Gets or sets the number of bits per pixel. + /// + public WebpBitsPerPixel BitsPerPixel { get; set; } = WebpBitsPerPixel.Bit32; + + /// + /// Gets or sets the color type. + /// + public WebpColorType ColorType { get; set; } = WebpColorType.Rgba; + /// /// Gets or sets the webp file format used. Either lossless or lossy. /// - public WebpFileFormatType? FileFormat { get; set; } + public WebpFileFormatType FileFormat { get; set; } = WebpFileFormatType.Lossless; /// /// Gets or sets the loop count. The number of times to loop the animation. 0 means infinitely. @@ -44,9 +56,6 @@ public class WebpMetadata : IDeepCloneable /// public Color BackgroundColor { get; set; } - /// - public IDeepCloneable DeepClone() => new WebpMetadata(this); - internal static WebpMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) => new() { @@ -54,4 +63,18 @@ public class WebpMetadata : IDeepCloneable BackgroundColor = metadata.BackgroundColor, RepeatCount = metadata.RepeatCount }; + + /// + public static WebpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + => throw new NotImplementedException(); + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => throw new NotImplementedException(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public WebpMetadata DeepClone() => new(this); } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 42cbd90f3..a0fbc210f 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -22,8 +22,8 @@ public class BmpEncoderTests public static readonly TheoryData BitsPerPixel = new() { - BmpBitsPerPixel.Pixel24, - BmpBitsPerPixel.Pixel32 + BmpBitsPerPixel.Bit24, + BmpBitsPerPixel.Bit32 }; public static readonly TheoryData RatioFiles = @@ -37,13 +37,13 @@ public class BmpEncoderTests public static readonly TheoryData BmpBitsPerPixelFiles = new() { - { Bit1, BmpBitsPerPixel.Pixel1 }, - { Bit2, BmpBitsPerPixel.Pixel2 }, - { Bit4, BmpBitsPerPixel.Pixel4 }, - { Bit8, BmpBitsPerPixel.Pixel8 }, - { Rgb16, BmpBitsPerPixel.Pixel16 }, - { Car, BmpBitsPerPixel.Pixel24 }, - { Bit32Rgb, BmpBitsPerPixel.Pixel32 } + { Bit1, BmpBitsPerPixel.Bit1 }, + { Bit2, BmpBitsPerPixel.Bit2 }, + { Bit4, BmpBitsPerPixel.Bit4 }, + { Bit8, BmpBitsPerPixel.Bit8 }, + { Rgb16, BmpBitsPerPixel.Bit16 }, + { Car, BmpBitsPerPixel.Bit24 }, + { Bit32Rgb, BmpBitsPerPixel.Bit32 } }; [Fact] @@ -97,61 +97,61 @@ public class BmpEncoderTests where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel); [Theory] - [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] public void Encode_32Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) // If supportTransparency is false, a v3 bitmap header will be written. where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] - [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] - [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Pixel32)] + [WithFile(Bit32Rgb, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(WinBmpv4, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] + [WithFile(WinBmpv5, PixelTypes.Rgba32 | PixelTypes.Rgb24, BmpBitsPerPixel.Bit32)] public void Encode_32Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] // WinBmpv3 is a 24 bits per pixel image. - [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Bit24)] // WinBmpv3 is a 24 bits per pixel image. + [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Bit24)] public void Encode_24Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] - [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] - [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel24)] + [WithFile(WinBmpv3, PixelTypes.Rgb24, BmpBitsPerPixel.Bit24)] + [WithFile(F, PixelTypes.Rgb24, BmpBitsPerPixel.Bit24)] public void Encode_24Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] - [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Bit16)] + [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Bit16)] public void Encode_16Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] - [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] - [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Pixel16)] + [WithFile(Rgb16, PixelTypes.Bgra5551, BmpBitsPerPixel.Bit16)] + [WithFile(Bit16, PixelTypes.Bgra5551, BmpBitsPerPixel.Bit16)] public void Encode_16Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] - [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] + [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Bit8)] + [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Bit8)] public void Encode_8Bit_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] - [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] - [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel8)] + [WithFile(WinBmpv5, PixelTypes.Rgba32, BmpBitsPerPixel.Bit8)] + [WithFile(Bit8Palette4, PixelTypes.Rgba32, BmpBitsPerPixel.Bit8)] public void Encode_8Bit_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Bit8)] public void Encode_8BitGray_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore( @@ -160,7 +160,7 @@ public class BmpEncoderTests supportTransparency: false); [Theory] - [WithFile(Bit4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel4)] + [WithFile(Bit4, PixelTypes.Rgba32, BmpBitsPerPixel.Bit4)] public void Encode_4Bit_WithV3Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) @@ -176,7 +176,7 @@ public class BmpEncoderTests } [Theory] - [WithFile(Bit4, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel4)] + [WithFile(Bit4, PixelTypes.Rgba32, BmpBitsPerPixel.Bit4)] public void Encode_4Bit_WithV4Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) @@ -192,7 +192,7 @@ public class BmpEncoderTests } [Theory] - [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Bit2)] public void Encode_2Bit_WithV3Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) @@ -214,7 +214,7 @@ public class BmpEncoderTests } [Theory] - [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Bit2)] public void Encode_2Bit_WithV4Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) @@ -236,21 +236,21 @@ public class BmpEncoderTests } [Theory] - [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel1)] + [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Bit1)] public void Encode_1Bit_WithV3Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: false); [Theory] - [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel1)] + [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Bit1)] public void Encode_1Bit_WithV4Header_Works( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Bit8)] public void Encode_8BitGray_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore( @@ -271,7 +271,7 @@ public class BmpEncoderTests using Image image = provider.GetImage(); BmpEncoder encoder = new() { - BitsPerPixel = BmpBitsPerPixel.Pixel8, + BitsPerPixel = BmpBitsPerPixel.Bit8, Quantizer = new WuQuantizer() }; @@ -303,7 +303,7 @@ public class BmpEncoderTests using Image image = provider.GetImage(); BmpEncoder encoder = new() { - BitsPerPixel = BmpBitsPerPixel.Pixel8, + BitsPerPixel = BmpBitsPerPixel.Bit8, Quantizer = new OctreeQuantizer() }; string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); @@ -322,8 +322,8 @@ public class BmpEncoderTests } [Theory] - [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] - [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] + [WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32, BmpBitsPerPixel.Bit32)] + [WithFile(Bit32Rgba, PixelTypes.Rgba32, BmpBitsPerPixel.Bit32)] public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); @@ -364,8 +364,8 @@ public class BmpEncoderTests } [Theory] - [WithFile(Car, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] - [WithFile(V5Header, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)] + [WithFile(Car, PixelTypes.Rgba32, BmpBitsPerPixel.Bit32)] + [WithFile(V5Header, PixelTypes.Rgba32, BmpBitsPerPixel.Bit32)] public void Encode_WorksWithDiscontiguousBuffers(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { @@ -374,7 +374,7 @@ public class BmpEncoderTests } [Theory] - [WithFile(BlackWhitePalletDataMatrix, PixelTypes.Rgb24, BmpBitsPerPixel.Pixel1)] + [WithFile(BlackWhitePalletDataMatrix, PixelTypes.Rgb24, BmpBitsPerPixel.Bit1)] public void Encode_Issue2467(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { @@ -409,7 +409,7 @@ public class BmpEncoderTests using Image image = provider.GetImage(); // There is no alpha in bmp with less then 32 bits per pixels, so the reference image will be made opaque. - if (bitsPerPixel != BmpBitsPerPixel.Pixel32) + if (bitsPerPixel != BmpBitsPerPixel.Bit32) { image.Mutate(c => c.MakeOpaque()); } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index 1d8435671..f7a398ec1 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -15,10 +15,10 @@ public class BmpMetadataTests public void CloneIsDeep() { BmpMetadata meta = new() - { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; + { BitsPerPixel = BmpBitsPerPixel.Bit24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; BmpMetadata clone = (BmpMetadata)meta.DeepClone(); - clone.BitsPerPixel = BmpBitsPerPixel.Pixel32; + clone.BitsPerPixel = BmpBitsPerPixel.Bit32; clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2; Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel)); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index a3fa082c6..c9f93446f 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -15,17 +15,17 @@ public class TgaEncoderTests public static readonly TheoryData BitsPerPixel = new() { - TgaBitsPerPixel.Pixel24, - TgaBitsPerPixel.Pixel32 + TgaBitsPerPixel.Bit24, + TgaBitsPerPixel.Bit32 }; public static readonly TheoryData TgaBitsPerPixelFiles = new() { - { Gray8BitBottomLeft, TgaBitsPerPixel.Pixel8 }, - { Bit16BottomLeft, TgaBitsPerPixel.Pixel16 }, - { Bit24BottomLeft, TgaBitsPerPixel.Pixel24 }, - { Bit32BottomLeft, TgaBitsPerPixel.Pixel32 }, + { Gray8BitBottomLeft, TgaBitsPerPixel.Bit8 }, + { Bit16BottomLeft, TgaBitsPerPixel.Bit16 }, + { Bit24BottomLeft, TgaBitsPerPixel.Bit24 }, + { Bit32BottomLeft, TgaBitsPerPixel.Bit32 }, }; [Theory] @@ -73,46 +73,46 @@ public class TgaEncoderTests [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + public void TgaEncoder_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false, compareTolerance: 0.03f); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + public void TgaEncoder_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + public void TgaEncoder_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + public void TgaEncoder_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + public void TgaEncoder_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false, compareTolerance: 0.03f); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + public void TgaEncoder_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] - public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Bit32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] @@ -152,8 +152,8 @@ public class TgaEncoderTests } [Theory] - [WithFile(Bit32BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] - [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Bit32)] + [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Bit24)] public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index bf24ba350..e5954719a 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -33,10 +33,10 @@ public class TgaFileHeaderTests } [Theory] - [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 }, 250, 195, TgaBitsPerPixel.Pixel32)] - [InlineData(new byte[] { 26, 1, 9, 0, 0, 0, 1, 16, 0, 0, 0, 0, 128, 0, 128, 0, 8, 0 }, 128, 128, TgaBitsPerPixel.Pixel8)] - [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 220, 0, 16, 0 }, 220, 220, TgaBitsPerPixel.Pixel16)] - [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 124, 0, 24, 32 }, 124, 124, TgaBitsPerPixel.Pixel24)] + [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 }, 250, 195, TgaBitsPerPixel.Bit32)] + [InlineData(new byte[] { 26, 1, 9, 0, 0, 0, 1, 16, 0, 0, 0, 0, 128, 0, 128, 0, 8, 0 }, 128, 128, TgaBitsPerPixel.Bit8)] + [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 220, 0, 16, 0 }, 220, 220, TgaBitsPerPixel.Bit16)] + [InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 124, 0, 24, 32 }, 124, 124, TgaBitsPerPixel.Bit24)] public void Identify_WithValidData_Works(byte[] data, int width, int height, TgaBitsPerPixel bitsPerPixel) { using MemoryStream stream = new(data); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 0dda304b6..99f4dee16 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -30,8 +30,8 @@ public class WebpDecoderTests [Theory] [InlineData(Lossless.GreenTransform1, 1000, 307, 32)] - [InlineData(Lossless.BikeThreeTransforms, 250, 195, 32)] - [InlineData(Lossless.NoTransform2, 128, 128, 32)] + [InlineData(Lossless.BikeThreeTransforms, 250, 195, 24)] + [InlineData(Lossless.NoTransform2, 128, 128, 24)] [InlineData(Lossy.Alpha1, 1000, 307, 32)] [InlineData(Lossy.Alpha2, 1000, 307, 32)] [InlineData(Lossy.BikeWithExif, 250, 195, 24)] From c01bdedbae48bfbbc8a4808ee9dde55fa864ad74 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 23:24:04 +1000 Subject: [PATCH 189/220] Update WebpMetadata.cs --- src/ImageSharp/Formats/Webp/WebpMetadata.cs | 90 +++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs index d8cd29d14..a7100dd26 100644 --- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Webp; /// /// Provides Webp specific metadata information for the image. /// -public class WebpMetadata : IFormatFrameMetadata +public class WebpMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -65,12 +67,90 @@ public class WebpMetadata : IFormatFrameMetadata }; /// - public static WebpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) - => throw new NotImplementedException(); + public static WebpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + WebpBitsPerPixel bitsPerPixel; + WebpColorType color; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + switch (colorType) + { + case PixelColorType.RGB: + case PixelColorType.BGR: + color = WebpColorType.Rgb; + bitsPerPixel = WebpBitsPerPixel.Bit24; + break; + case PixelColorType.YCbCr: + color = WebpColorType.Yuv; + bitsPerPixel = WebpBitsPerPixel.Bit24; + break; + default: + if (colorType.HasFlag(PixelColorType.Alpha)) + { + color = WebpColorType.Rgba; + bitsPerPixel = WebpBitsPerPixel.Bit32; + break; + } + + color = WebpColorType.Rgb; + bitsPerPixel = WebpBitsPerPixel.Bit24; + break; + } + + return new() + { + BitsPerPixel = bitsPerPixel, + ColorType = color, + FileFormat = WebpFileFormatType.Lossless, + BackgroundColor = metadata.BackgroundColor, + RepeatCount = metadata.RepeatCount + }; + } + + /// + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp; + PixelColorType colorType; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + PixelComponentInfo info; + switch (this.ColorType) + { + case WebpColorType.Yuv: + bpp = 24; + colorType = PixelColorType.YCbCr; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + case WebpColorType.Rgb: + bpp = 24; + colorType = PixelColorType.RGB; + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + break; + case WebpColorType.Rgba: + default: + bpp = 32; + colorType = PixelColorType.RGB | PixelColorType.Alpha; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, + }; + } /// - public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() - => throw new NotImplementedException(); + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + ColorTableMode = FrameColorTableMode.Global, + RepeatCount = this.RepeatCount, + BackgroundColor = this.BackgroundColor + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); From baaf29cec50180e82505bc2a7f9d53b7d3c57113 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 12 Jun 2024 09:40:43 +1000 Subject: [PATCH 190/220] Fix default webp file format --- src/ImageSharp/Formats/Webp/WebpMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs index a7100dd26..a42435fd4 100644 --- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs @@ -43,7 +43,7 @@ public class WebpMetadata : IFormatMetadata /// /// Gets or sets the webp file format used. Either lossless or lossy. /// - public WebpFileFormatType FileFormat { get; set; } = WebpFileFormatType.Lossless; + public WebpFileFormatType FileFormat { get; set; } = WebpFileFormatType.Lossy; /// /// Gets or sets the loop count. The number of times to loop the animation. 0 means infinitely. From 9a0e289d25f54ef89dfea1e85ca8d673a745a70c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Jun 2024 11:01:35 +1000 Subject: [PATCH 191/220] Complete conversion APIs --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 1 - src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 10 +- .../Formats/Bmp/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/EncodingType.cs | 20 ++ .../Formats/FormatConnectingMetadata.cs | 7 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 49 +-- src/ImageSharp/Formats/Gif/GifMetadata.cs | 28 -- .../Formats/Gif/MetadataExtensions.cs | 97 ------ .../Formats/IFormatFrameMetadata.cs | 2 +- src/ImageSharp/Formats/IFormatMetadata.cs | 2 +- src/ImageSharp/Formats/ImageDecoder.cs | 21 +- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 3 +- .../Formats/Jpeg/MetadataExtensions.cs | 21 -- .../Formats/Pbm/IPbmEncoderOptions.cs | 25 -- .../Formats/Pbm/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/Pbm/PbmEncoder.cs | 2 - src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs | 1 - src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 2 +- .../Formats/Png/MetadataExtensions.cs | 84 ------ src/ImageSharp/Formats/Png/PngDecoder.cs | 2 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 56 +--- .../Formats/Png/PngFrameMetadata.cs | 24 +- src/ImageSharp/Formats/Png/PngMetadata.cs | 35 +-- .../Formats/Qoi/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/Qoi/QoiMetadata.cs | 2 +- .../Formats/Tga/MetadataExtensions.cs | 20 -- .../Formats/Tiff/MetadataExtensions.cs | 27 -- .../Formats/Tiff/TiffEncoderCore.cs | 1 - .../Formats/Webp/Chunks/WebpFrameData.cs | 20 +- .../Formats/Webp/Lossless/Vp8LEncoder.cs | 2 +- .../Formats/Webp/Lossy/Vp8Encoder.cs | 6 +- .../Formats/Webp/MetadataExtensions.cs | 75 ----- .../Formats/Webp/WebpAnimationDecoder.cs | 4 +- .../Formats/Webp/WebpBlendMethod.cs | 22 -- .../Formats/Webp/WebpCommonUtils.cs | 50 ---- .../Formats/Webp/WebpDisposalMethod.cs | 20 -- .../Formats/Webp/WebpEncoderCore.cs | 23 +- .../Formats/Webp/WebpFileFormatType.cs | 4 +- .../Formats/Webp/WebpFrameMetadata.cs | 37 ++- src/ImageSharp/Formats/Webp/WebpMetadata.cs | 17 +- .../{ => _Generated}/ImageExtensions.Save.cs | 2 - .../{ => _Generated}/ImageExtensions.Save.tt | 19 +- .../_Generated/ImageMetadataExtensions.cs | 283 ++++++++++++++++++ .../_Generated/ImageMetadataExtensions.tt | 77 +++++ .../Formats/_Generated/_Formats.ttinclude | 24 ++ src/ImageSharp/ImageSharp.csproj | 13 +- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 59 ++-- src/ImageSharp/Metadata/ImageMetadata.cs | 52 ++-- src/ImageSharp/PixelFormats/PixelTypeInfo.cs | 4 +- .../Formats/Bmp/BmpMetadataTests.cs | 2 +- .../Formats/Gif/GifEncoderTests.cs | 26 +- .../Formats/Pbm/PbmMetadataTests.cs | 1 - .../Formats/Png/PngEncoderTests.cs | 8 +- .../Formats/WebP/WebpEncoderTests.cs | 28 +- 54 files changed, 606 insertions(+), 874 deletions(-) delete mode 100644 src/ImageSharp/Formats/Bmp/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/EncodingType.cs delete mode 100644 src/ImageSharp/Formats/Gif/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Pbm/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Png/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Qoi/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Tga/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Tiff/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Webp/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Webp/WebpBlendMethod.cs delete mode 100644 src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs rename src/ImageSharp/Formats/{ => _Generated}/ImageExtensions.Save.cs (99%) rename src/ImageSharp/Formats/{ => _Generated}/ImageExtensions.Save.tt (95%) create mode 100644 src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt create mode 100644 src/ImageSharp/Formats/_Generated/_Formats.ttinclude diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 24a4fa2f5..298bb8dbc 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index bad47bd0d..c19ab44cf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Provides Bmp specific metadata information for the image. /// -public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata +public class BmpMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -66,10 +66,6 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata - public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) - => new(); - /// public PixelTypeInfo GetPixelTypeInfo() { @@ -140,10 +136,6 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata - public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() - => new(); - /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs deleted file mode 100644 index 5297d0c98..000000000 --- a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the bmp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance); -} diff --git a/src/ImageSharp/Formats/EncodingType.cs b/src/ImageSharp/Formats/EncodingType.cs new file mode 100644 index 000000000..f4567ca43 --- /dev/null +++ b/src/ImageSharp/Formats/EncodingType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Provides a way to specify the type of encoding to be used. +/// +public enum EncodingType +{ + /// + /// Lossless encoding, which compresses data without any loss of information. + /// + Lossless, + + /// + /// Lossy encoding, which compresses data by discarding some of it. + /// + Lossy +} diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs index 2acbe9699..07579c09e 100644 --- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -11,7 +11,12 @@ namespace SixLabors.ImageSharp.Formats; public class FormatConnectingMetadata { /// - /// Gets the quality. + /// Gets the encoding type. + /// + public EncodingType EncodingType { get; init; } + + /// + /// Gets the quality to use when is . /// /// /// The value is usually between 1 and 100. Defaults to 100. diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 5fd20d6ae..90177ccc1 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -5,8 +5,6 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; @@ -85,7 +83,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - GifMetadata gifMetadata = GetGifMetadata(image); + GifMetadata gifMetadata = image.Metadata.CloneGifMetadata(); this.colorTableMode ??= gifMetadata.ColorTableMode; bool useGlobalTable = this.colorTableMode == FrameColorTableMode.Global; @@ -178,56 +176,17 @@ internal sealed class GifEncoderCore : IImageEncoderInternals quantized?.Dispose(); } - private static GifMetadata GetGifMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - return gif.DeepClone(); - } - - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - AnimatedImageMetadata ani = png.ToAnimatedImageMetadata(); - return GifMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata(); - return GifMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - private static GifFrameMetadata GetGifFrameMetadata(ImageFrame frame, int transparencyIndex) where TPixel : unmanaged, IPixel { - GifFrameMetadata? metadata = null; - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - metadata = gif.DeepClone(); - } - else if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); - metadata = GifFrameMetadata.FromAnimatedMetadata(ani); - } - else if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); - metadata = GifFrameMetadata.FromAnimatedMetadata(ani); - } - - if (metadata?.ColorTableMode == FrameColorTableMode.Global && transparencyIndex > -1) + GifFrameMetadata metadata = frame.Metadata.CloneGifMetadata(); + if (metadata.ColorTableMode == FrameColorTableMode.Global && transparencyIndex > -1) { metadata.HasTransparency = true; metadata.TransparencyIndex = ClampIndex(transparencyIndex); } - return metadata ?? new(); + return metadata; } private void EncodeAdditionalFrames( diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 90d3312c8..565038b55 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -69,34 +69,6 @@ public class GifMetadata : IFormatMetadata /// public IList Comments { get; set; } = []; - internal static GifMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - { - int index = 0; - Color background = metadata.BackgroundColor; - if (metadata.ColorTable.HasValue) - { - ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; - for (int i = 0; i < colorTable.Length; i++) - { - if (background != colorTable[i]) - { - continue; - } - - index = i; - break; - } - } - - return new() - { - GlobalColorTable = metadata.ColorTable, - ColorTableMode = metadata.ColorTableMode, - RepeatCount = metadata.RepeatCount, - BackgroundColorIndex = (byte)Numerics.Clamp(index, 0, 255), - }; - } - /// public static GifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs deleted file mode 100644 index 0f2d281f1..000000000 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the gif format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static GifMetadata GetGifMetadata(this ImageMetadata source) - => source.GetFormatMetadata(GifFormat.Instance); - - /// - /// Gets the gif format specific metadata for the image. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified image, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the gif metadata exists; otherwise, . - /// - public static bool TryGetGifMetadata(this ImageMetadata source, [NotNullWhen(true)] out GifMetadata? metadata) - => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); - - /// - /// Gets the gif format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source) - => source.GetFormatMetadata(GifFormat.Instance); - - /// - /// Gets the gif format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified frame, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the gif frame metadata exists; otherwise, . - /// - public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out GifFrameMetadata? metadata) - => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this GifMetadata source) - { - Color background = Color.Transparent; - if (source.GlobalColorTable != null) - { - background = source.GlobalColorTable.Value.Span[source.BackgroundColorIndex]; - } - - return new() - { - ColorTable = source.GlobalColorTable, - ColorTableMode = source.ColorTableMode, - RepeatCount = source.RepeatCount, - BackgroundColor = background, - }; - } - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source) - { - // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or - // has a local palette with 256 colors and is not transparent we should use 'Source'. - bool blendSource = source.DisposalMode == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); - - // If the color table is global and frame has no transparency. Consider it 'Source' also. - blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency; - - return new() - { - ColorTable = source.LocalColorTable, - ColorTableMode = source.ColorTableMode, - Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), - DisposalMode = source.DisposalMode, - BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, - }; - } -} diff --git a/src/ImageSharp/Formats/IFormatFrameMetadata.cs b/src/ImageSharp/Formats/IFormatFrameMetadata.cs index 4c5321073..4eef93ad3 100644 --- a/src/ImageSharp/Formats/IFormatFrameMetadata.cs +++ b/src/ImageSharp/Formats/IFormatFrameMetadata.cs @@ -20,7 +20,7 @@ public interface IFormatFrameMetadata : IDeepCloneable /// /// The metadata type implementing this interface. public interface IFormatFrameMetadata : IFormatFrameMetadata, IDeepCloneable - where TSelf : class, IFormatFrameMetadata, new() + where TSelf : class, IFormatFrameMetadata { /// /// Creates a new instance of the class from the given . diff --git a/src/ImageSharp/Formats/IFormatMetadata.cs b/src/ImageSharp/Formats/IFormatMetadata.cs index e0c32e8b2..8d695306e 100644 --- a/src/ImageSharp/Formats/IFormatMetadata.cs +++ b/src/ImageSharp/Formats/IFormatMetadata.cs @@ -28,7 +28,7 @@ public interface IFormatMetadata : IDeepCloneable /// /// The metadata type implementing this interface. public interface IFormatMetadata : IFormatMetadata, IDeepCloneable - where TSelf : class, IFormatMetadata, new() + where TSelf : class, IFormatMetadata { /// /// Creates a new instance of the class from the given . diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index ebb45d701..4e79f79ef 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -231,7 +232,7 @@ public abstract class ImageDecoder : IImageDecoder throw new NotSupportedException("Cannot read from the stream."); } - Task PeformActionAndResetPosition(Stream s, long position, CancellationToken ct) + Task PerformActionAndResetPosition(Stream s, long position, CancellationToken ct) { try { @@ -263,15 +264,15 @@ public abstract class ImageDecoder : IImageDecoder // code below to copy the stream to an in-memory buffer before invoking the action. if (stream is MemoryStream ms) { - return PeformActionAndResetPosition(ms, ms.Position, cancellationToken); + return PerformActionAndResetPosition(ms, ms.Position, cancellationToken); } if (stream is ChunkedMemoryStream cms) { - return PeformActionAndResetPosition(cms, cms.Position, cancellationToken); + return PerformActionAndResetPosition(cms, cms.Position, cancellationToken); } - return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken); + return CopyToMemoryStreamAndActionAsync(options, stream, PerformActionAndResetPosition, cancellationToken); } private static async Task CopyToMemoryStreamAndActionAsync( @@ -282,7 +283,7 @@ public abstract class ImageDecoder : IImageDecoder { long position = stream.CanSeek ? stream.Position : 0; Configuration configuration = options.Configuration; - using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); + await using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; return await action(memoryStream, position, cancellationToken).ConfigureAwait(false); @@ -293,6 +294,11 @@ public abstract class ImageDecoder : IImageDecoder if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format)) { image.Metadata.DecodedImageFormat = format; + + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.DecodedImageFormat = format; + } } } @@ -301,6 +307,11 @@ public abstract class ImageDecoder : IImageDecoder if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format)) { info.Metadata.DecodedImageFormat = format; + + foreach (ImageFrameMetadata frame in info.FrameMetadataCollection) + { + frame.DecodedImageFormat = format; + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 8b123fa35..f2f34ec49 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -112,7 +112,7 @@ public class JpegMetadata : IFormatMetadata public static JpegMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { JpegColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.YCbCr; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { case PixelColorType.Luminance: @@ -194,6 +194,7 @@ public class JpegMetadata : IFormatMetadata public FormatConnectingMetadata ToFormatConnectingMetadata() => new() { + EncodingType = EncodingType.Lossy, PixelTypeInfo = this.GetPixelTypeInfo(), Quality = this.Quality, }; diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs deleted file mode 100644 index 7330e74b7..000000000 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Text; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the jpeg format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs b/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs deleted file mode 100644 index 7039ef262..000000000 --- a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Pbm; - -/// -/// Configuration options for use during PBM encoding. -/// -internal interface IPbmEncoderOptions -{ - /// - /// Gets the encoding of the pixels. - /// - PbmEncoding? Encoding { get; } - - /// - /// Gets the Color type of the resulting image. - /// - PbmColorType? ColorType { get; } - - /// - /// Gets the Data Type of the pixel components. - /// - PbmComponentType? ComponentType { get; } -} diff --git a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs b/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs deleted file mode 100644 index 6d44e91a5..000000000 --- a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Pbm; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the pbm format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PbmFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs index 8258c9165..f7a9d7936 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; - namespace SixLabors.ImageSharp.Formats.Pbm; /// diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs index b6e31a3c2..5ae37d4e6 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers.Text; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Pbm; diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 49a36de41..9b23aecac 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -46,7 +46,7 @@ public class PbmMetadata : IFormatMetadata public static PbmMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { PbmColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.Luminance; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs deleted file mode 100644 index 6da1fb5fd..000000000 --- a/src/ImageSharp/Formats/Png/MetadataExtensions.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the png format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance); - - /// - /// Gets the png format specific metadata for the image. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the png metadata exists; otherwise, . - /// - public static bool TryGetPngMetadata(this ImageMetadata source, [NotNullWhen(true)] out PngMetadata? metadata) - => source.TryGetFormatMetadata(PngFormat.Instance, out metadata); - - /// - /// Gets the png format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance); - - /// - /// Gets the png format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the png frame metadata exists; otherwise, . - /// - public static bool TryGetPngMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out PngFrameMetadata? metadata) - => source.TryGetFormatMetadata(PngFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this PngMetadata source) - => new() - { - ColorTable = source.ColorTable, - ColorTableMode = FrameColorTableMode.Global, - RepeatCount = (ushort)Numerics.Clamp(source.RepeatCount, 0, ushort.MaxValue), - }; - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this PngFrameMetadata source) - { - double delay = source.FrameDelay.ToDouble(); - if (double.IsNaN(delay)) - { - delay = 0; - } - - return new() - { - ColorTableMode = FrameColorTableMode.Global, - Duration = TimeSpan.FromMilliseconds(delay * 1000), - DisposalMode = GetMode(source.DisposalMode), - BlendMode = source.BlendMode, - }; - } - - private static FrameDisposalMode GetMode(FrameDisposalMode method) => method switch - { - FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, - FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - _ => FrameDisposalMode.DoNotDispose, - }; -} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index e62a14b5e..cfea0e602 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -101,5 +101,5 @@ public sealed class PngDecoder : SpecializedImageDecoder } /// - protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new PngDecoderOptions() { GeneralOptions = options }; + protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new() { GeneralOptions = options }; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index d1528d08b..a5ab73988 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -9,10 +9,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -160,7 +158,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable this.height = image.Height; ImageMetadata metadata = image.Metadata; - PngMetadata pngMetadata = GetPngMetadata(image); + PngMetadata pngMetadata = metadata.ClonePngMetadata(); this.SanitizeAndSetEncoderOptions(this.encoder, pngMetadata, out this.use16Bit, out this.bytesPerPixel); stream.Write(PngConstants.HeaderBytes); @@ -211,7 +209,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { // Write the first animated frame. currentFrame = image.Frames[currentFrameIndex]; - PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame); + PngFrameMetadata frameMetadata = currentFrame.Metadata.GetPngMetadata(); FrameDisposalMode previousDisposal = frameMetadata.DisposalMode; FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); uint sequenceNumber = 1; @@ -241,7 +239,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable currentFrame = image.Frames[currentFrameIndex]; ImageFrame? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null; - frameMetadata = GetPngFrameMetadata(currentFrame); + frameMetadata = currentFrame.Metadata.GetPngMetadata(); bool blend = frameMetadata.BlendMode == FrameBlendMode.Over; (bool difference, Rectangle bounds) = @@ -288,54 +286,6 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable this.currentScanline?.Dispose(); } - private static PngMetadata GetPngMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - return png.DeepClone(); - } - - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata(); - return PngMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata(); - return PngMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - - private static PngFrameMetadata GetPngFrameMetadata(ImageFrame frame) - where TPixel : unmanaged, IPixel - { - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - return png.DeepClone(); - } - - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata(); - return PngFrameMetadata.FromAnimatedMetadata(ani); - } - - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); - return PngFrameMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - /// /// Convert transparent pixels, to transparent black pixels, which can yield to better compression in some cases. /// diff --git a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs index de141909a..c142a1c8e 100644 --- a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs @@ -57,22 +57,6 @@ public class PngFrameMetadata : IFormatFrameMetadata this.BlendMode = frameControl.BlendMode; } - internal static PngFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) - => new() - { - FrameDelay = new(metadata.Duration.TotalMilliseconds / 1000), - DisposalMode = GetMode(metadata.DisposalMode), - BlendMode = metadata.BlendMode, - }; - - private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch - { - FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, - _ => FrameDisposalMode.DoNotDispose, - }; - /// public static PngFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) => new() @@ -105,4 +89,12 @@ public class PngFrameMetadata : IFormatFrameMetadata /// public PngFrameMetadata DeepClone() => new(this); + + private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch + { + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + _ => FrameDisposalMode.DoNotDispose, + }; } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index d72dc5184..beb4ca5ae 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -90,38 +90,6 @@ public class PngMetadata : IFormatMetadata /// public bool AnimateRootFrame { get; set; } = true; - internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - { - // Should the conversion be from a format that uses a 24bit palette entries (gif) - // we need to clone and adjust the color table to allow for transparency. - Color[]? colorTable = metadata.ColorTable?.ToArray(); - if (colorTable != null) - { - for (int i = 0; i < colorTable.Length; i++) - { - ref Color c = ref colorTable[i]; - if (c != metadata.BackgroundColor) - { - continue; - } - - // Png treats background as fully empty - c = Color.Transparent; - break; - } - } - - return new() - { - ColorType = colorTable != null ? PngColorType.Palette : PngColorType.RgbWithAlpha, - BitDepth = colorTable != null - ? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8) - : PngBitDepth.Bit8, - ColorTable = colorTable, - RepeatCount = metadata.RepeatCount, - }; - } - /// public static PngMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { @@ -145,8 +113,7 @@ public class PngMetadata : IFormatMetadata } PngColorType color; - PixelColorType colorType = - metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { diff --git a/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs b/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs deleted file mode 100644 index 1e0fa8899..000000000 --- a/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Qoi; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the qoi format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static QoiMetadata GetQoiMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(QoiFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs index c8bcd9748..e2062014d 100644 --- a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs +++ b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs @@ -40,7 +40,7 @@ public class QoiMetadata : IFormatMetadata /// public static QoiMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { - PixelColorType color = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB; + PixelColorType color = metadata.PixelTypeInfo.ColorType; if (color.HasFlag(PixelColorType.Alpha)) { diff --git a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs deleted file mode 100644 index 8d5e35764..000000000 --- a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the tga format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs deleted file mode 100644 index b06f5dd47..000000000 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the tiff format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); - - /// - /// Gets the tiff format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 546ff7947..c8eeb1e31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -395,7 +395,6 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit48: // Encoding not yet supported bits per pixel will default to 24 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); break; case TiffBitsPerPixel.Bit64: diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs index c8ff579a8..66662569c 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs @@ -10,7 +10,7 @@ internal readonly struct WebpFrameData /// public const uint HeaderSize = 16; - public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod) + public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, FrameBlendMode blendingMethod, FrameDisposalMode disposalMethod) { this.DataSize = dataSize; this.X = x; @@ -30,12 +30,12 @@ internal readonly struct WebpFrameData width, height, duration, - (flags & 2) == 0 ? WebpBlendMethod.Over : WebpBlendMethod.Source, - (flags & 1) == 1 ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose) + (flags & 2) == 0 ? FrameBlendMode.Over : FrameBlendMode.Source, + (flags & 1) == 1 ? FrameDisposalMode.RestoreToBackground : FrameDisposalMode.DoNotDispose) { } - public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod) + public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, FrameBlendMode blendingMethod, FrameDisposalMode disposalMethod) : this(0, x, y, width, height, duration, blendingMethod, disposalMethod) { } @@ -74,12 +74,12 @@ internal readonly struct WebpFrameData /// /// Gets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. /// - public WebpBlendMethod BlendingMethod { get; } + public FrameBlendMode BlendingMethod { get; } /// /// Gets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. /// - public WebpDisposalMethod DisposalMethod { get; } + public FrameDisposalMode DisposalMethod { get; } public Rectangle Bounds => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height); @@ -91,13 +91,13 @@ internal readonly struct WebpFrameData { byte flags = 0; - if (this.BlendingMethod is WebpBlendMethod.Source) + if (this.BlendingMethod is FrameBlendMode.Source) { // Set blending flag. flags |= 2; } - if (this.DisposalMethod is WebpDisposalMethod.RestoreToBackground) + if (this.DisposalMethod is FrameDisposalMode.RestoreToBackground) { // Set disposal flag. flags |= 1; @@ -124,7 +124,7 @@ internal readonly struct WebpFrameData { Span buffer = stackalloc byte[4]; - WebpFrameData data = new( + return new( dataSize: WebpChunkParsingUtils.ReadChunkSize(stream, buffer), x: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2, y: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2, @@ -132,7 +132,5 @@ internal readonly struct WebpFrameData height: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) + 1, duration: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer), flags: stream.ReadByte()); - - return data; } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index f658e40f6..58d007265 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -259,7 +259,7 @@ internal class Vp8LEncoder : IDisposable if (hasAnimation) { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index 40d91ecf1..7e1ef93cf 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -332,7 +332,7 @@ internal class Vp8Encoder : IDisposable if (hasAnimation) { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } @@ -376,7 +376,7 @@ internal class Vp8Encoder : IDisposable where TPixel : unmanaged, IPixel { ImageFrame frame = image.Frames.RootFrame; - this.Encode(stream, frame, image.Bounds, WebpCommonUtils.GetWebpFrameMetadata(frame), false, image); + this.Encode(stream, frame, image.Bounds, frame.Metadata.GetWebpMetadata(), false, image); } /// @@ -462,7 +462,7 @@ internal class Vp8Encoder : IDisposable // Extract and encode alpha channel data, if present. int alphaDataSize = 0; bool alphaCompressionSucceeded = false; - Span alphaData = Span.Empty; + Span alphaData = []; IMemoryOwner encodedAlphaData = null; try { diff --git a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs deleted file mode 100644 index 731d3f1ff..000000000 --- a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Webp; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the webp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance); - - /// - /// Gets the webp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the webp metadata exists; otherwise, . - /// - public static bool TryGetWebpMetadata(this ImageMetadata source, [NotNullWhen(true)] out WebpMetadata? metadata) - => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata); - - /// - /// Gets the webp format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance); - - /// - /// Gets the webp format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the webp frame metadata exists; otherwise, . - /// - public static bool TryGetWebpFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out WebpFrameMetadata? metadata) - => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this WebpMetadata source) - => new() - { - ColorTableMode = FrameColorTableMode.Global, - RepeatCount = source.RepeatCount, - BackgroundColor = source.BackgroundColor - }; - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this WebpFrameMetadata source) - => new() - { - ColorTableMode = FrameColorTableMode.Global, - Duration = TimeSpan.FromMilliseconds(source.FrameDelay), - DisposalMode = GetMode(source.DisposalMethod), - BlendMode = source.BlendMethod == WebpBlendMethod.Over ? FrameBlendMode.Over : FrameBlendMode.Source, - }; - - private static FrameDisposalMode GetMode(WebpDisposalMethod method) => method switch - { - WebpDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - WebpDisposalMethod.DoNotDispose => FrameDisposalMode.DoNotDispose, - _ => FrameDisposalMode.DoNotDispose, - }; -} diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 70372fe98..72405e480 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -195,14 +195,14 @@ internal class WebpAnimationDecoder : IDisposable Rectangle regionRectangle = frameData.Bounds; - if (frameData.DisposalMethod is WebpDisposalMethod.RestoreToBackground) + if (frameData.DisposalMethod is FrameDisposalMode.RestoreToBackground) { this.RestoreToBackground(imageFrame, backgroundColor); } using Buffer2D decodedImageFrame = this.DecodeImageFrameData(frameData, webpInfo); - bool blend = previousFrame != null && frameData.BlendingMethod == WebpBlendMethod.Over; + bool blend = previousFrame != null && frameData.BlendingMethod == FrameBlendMode.Over; DrawDecodedImageFrameOnCanvas(decodedImageFrame, imageFrame, regionRectangle, blend); previousFrame = currentFrame ?? image.Frames.RootFrame; diff --git a/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs b/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs deleted file mode 100644 index f16f7650c..000000000 --- a/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp; - -/// -/// Indicates how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. -/// -public enum WebpBlendMethod -{ - /// - /// Do not blend. After disposing of the previous frame, - /// render the current frame on the canvas by overwriting the rectangle covered by the current frame. - /// - Source = 0, - - /// - /// Use alpha blending. After disposing of the previous frame, render the current frame on the canvas using alpha-blending. - /// If the current frame does not have an alpha channel, assume alpha value of 255, effectively replacing the rectangle. - /// - Over = 1, -} diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 49482260b..a1e9821c0 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -4,8 +4,6 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -15,54 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// internal static class WebpCommonUtils { - public static WebpMetadata GetWebpMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - return (WebpMetadata)webp.DeepClone(); - } - - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata(); - return WebpMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - AnimatedImageMetadata ani = png.ToAnimatedImageMetadata(); - return WebpMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - - public static WebpFrameMetadata GetWebpFrameMetadata(ImageFrame frame) - where TPixel : unmanaged, IPixel - { - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - return (WebpFrameMetadata)webp.DeepClone(); - } - - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata(); - return WebpFrameMetadata.FromAnimatedMetadata(ani); - } - - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); - return WebpFrameMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - /// /// Checks if the pixel row is not opaque. /// diff --git a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs b/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs deleted file mode 100644 index 47cc83951..000000000 --- a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp; - -/// -/// Indicates how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. -/// -public enum WebpDisposalMethod -{ - /// - /// Do not dispose. Leave the canvas as is. - /// - DoNotDispose = 0, - - /// - /// Dispose to background color. Fill the rectangle on the canvas covered by the current frame with background color specified in the ANIM chunk. - /// - RestoreToBackground = 1 -} diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index d29759f9a..21ce444b1 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -124,7 +125,7 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals } else { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); lossless = webpMetadata.FileFormat == WebpFileFormatType.Lossless; } @@ -150,12 +151,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; - WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); + WebpFrameMetadata frameMetadata = previousFrame.Metadata.GetWebpMetadata(); hasAlpha |= encoder.Encode(previousFrame, previousFrame.Bounds(), frameMetadata, stream, hasAnimation); if (hasAnimation) { - WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod; + FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod; // Encode additional frames // This frame is reused to store de-duplicated pixel buffers. @@ -163,12 +164,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals for (int i = 1; i < image.Frames.Count; i++) { - ImageFrame? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame; ImageFrame currentFrame = image.Frames[i]; ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; - frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame); - bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over; + frameMetadata = currentFrame.Metadata.GetWebpMetadata(); + bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over; (bool difference, Rectangle bounds) = AnimationUtilities.DeDuplicatePixels( @@ -227,8 +228,8 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; - WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); - WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod; + WebpFrameMetadata frameMetadata = previousFrame.Metadata.GetWebpMetadata(); + FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod; hasAlpha |= encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata); @@ -238,12 +239,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals for (int i = 1; i < image.Frames.Count; i++) { - ImageFrame? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame; ImageFrame currentFrame = image.Frames[i]; ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; - frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame); - bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over; + frameMetadata = currentFrame.Metadata.GetWebpMetadata(); + bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over; (bool difference, Rectangle bounds) = AnimationUtilities.DeDuplicatePixels( diff --git a/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs b/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs index 1ed9bbb43..6f606cdf4 100644 --- a/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs +++ b/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Webp; public enum WebpFileFormatType { /// - /// The lossless webp format. + /// The lossless Webp format, which compresses data without any loss of information. /// Lossless, /// - /// The lossy webp format. + /// The lossy Webp format, which compresses data by discarding some of it. /// Lossy, } diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs index cd1b5d590..45e182d22 100644 --- a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Provides webp specific metadata information for the image frame. /// -public class WebpFrameMetadata : IDeepCloneable +public class WebpFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -29,12 +29,12 @@ public class WebpFrameMetadata : IDeepCloneable /// /// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. /// - public WebpBlendMethod BlendMethod { get; set; } + public FrameBlendMode BlendMethod { get; set; } /// /// Gets or sets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. /// - public WebpDisposalMethod DisposalMethod { get; set; } + public FrameDisposalMode DisposalMethod { get; set; } /// /// Gets or sets the frame duration. The time to wait before displaying the next frame, @@ -43,13 +43,34 @@ public class WebpFrameMetadata : IDeepCloneable public uint FrameDelay { get; set; } /// - public IDeepCloneable DeepClone() => new WebpFrameMetadata(this); - - internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) + public static WebpFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) => new() { FrameDelay = (uint)metadata.Duration.TotalMilliseconds, - BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over, - DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose + BlendMethod = metadata.BlendMode, + DisposalMethod = GetMode(metadata.DisposalMode) + }; + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => new() + { + ColorTableMode = FrameColorTableMode.Global, + Duration = TimeSpan.FromMilliseconds(this.FrameDelay), + DisposalMode = this.DisposalMethod, + BlendMode = this.BlendMethod, }; + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public WebpFrameMetadata DeepClone() => new(this); + + private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch + { + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + _ => FrameDisposalMode.DoNotDispose, + }; } diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs index a42435fd4..33ebbbf6d 100644 --- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs @@ -54,24 +54,16 @@ public class WebpMetadata : IFormatMetadata /// Gets or sets the default background color of the canvas when animating. /// This color may be used to fill the unused space on the canvas around the frames, /// as well as the transparent pixels of the first frame. - /// The background color is also used when the Disposal method is . + /// The background color is also used when the Disposal method is . /// public Color BackgroundColor { get; set; } - internal static WebpMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - => new() - { - FileFormat = WebpFileFormatType.Lossless, - BackgroundColor = metadata.BackgroundColor, - RepeatCount = metadata.RepeatCount - }; - /// public static WebpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { WebpBitsPerPixel bitsPerPixel; WebpColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { case PixelColorType.RGB: @@ -100,9 +92,9 @@ public class WebpMetadata : IFormatMetadata { BitsPerPixel = bitsPerPixel, ColorType = color, - FileFormat = WebpFileFormatType.Lossless, BackgroundColor = metadata.BackgroundColor, - RepeatCount = metadata.RepeatCount + RepeatCount = metadata.RepeatCount, + FileFormat = metadata.EncodingType == EncodingType.Lossless ? WebpFileFormatType.Lossless : WebpFileFormatType.Lossy }; } @@ -146,6 +138,7 @@ public class WebpMetadata : IFormatMetadata public FormatConnectingMetadata ToFormatConnectingMetadata() => new() { + EncodingType = this.FileFormat == WebpFileFormatType.Lossless ? EncodingType.Lossless : EncodingType.Lossy, PixelTypeInfo = this.GetPixelTypeInfo(), ColorTableMode = FrameColorTableMode.Global, RepeatCount = this.RepeatCount, diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs similarity index 99% rename from src/ImageSharp/Formats/ImageExtensions.Save.cs rename to src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs index 7e5989d6f..5d7f84acf 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs @@ -2,8 +2,6 @@ // Licensed under the Six Labors Split License. // -using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt similarity index 95% rename from src/ImageSharp/Formats/ImageExtensions.Save.tt rename to src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt index d4f1ed233..144dd8362 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt @@ -1,25 +1,8 @@ -<#@ template language="C#" #> +<#@include file="_Formats.ttinclude" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - // -using SixLabors.ImageSharp.Advanced; - <# - var formats = new []{ - "Bmp", - "Gif", - "Jpeg", - "Pbm", - "Png", - "Qoi", - "Tga", - "Tiff", - "Webp", - }; - foreach (string fmt in formats) { #> diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs new file mode 100644 index 000000000..826f5905b --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs @@ -0,0 +1,283 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Pbm; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Qoi; +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Webp; + +namespace SixLabors.ImageSharp; + +/// +/// Extension methods for the and types. +/// +public static class ImageMetadataExtensions +{ + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static BmpMetadata GetBmpMetadata(this ImageMetadata source) => source.GetFormatMetadata(BmpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static BmpMetadata CloneBmpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(BmpFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static GifMetadata GetGifMetadata(this ImageMetadata source) => source.GetFormatMetadata(GifFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static GifMetadata CloneGifMetadata(this ImageMetadata source) => source.CloneFormatMetadata(GifFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static JpegMetadata GetJpegMetadata(this ImageMetadata source) => source.GetFormatMetadata(JpegFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static JpegMetadata CloneJpegMetadata(this ImageMetadata source) => source.CloneFormatMetadata(JpegFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static PbmMetadata GetPbmMetadata(this ImageMetadata source) => source.GetFormatMetadata(PbmFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static PbmMetadata ClonePbmMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PbmFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static PngMetadata ClonePngMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PngFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static QoiMetadata GetQoiMetadata(this ImageMetadata source) => source.GetFormatMetadata(QoiFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static QoiMetadata CloneQoiMetadata(this ImageMetadata source) => source.CloneFormatMetadata(QoiFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static TgaMetadata GetTgaMetadata(this ImageMetadata source) => source.GetFormatMetadata(TgaFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static TgaMetadata CloneTgaMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TgaFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static TiffMetadata GetTiffMetadata(this ImageMetadata source) => source.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static TiffMetadata CloneTiffMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TiffFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static WebpMetadata GetWebpMetadata(this ImageMetadata source) => source.GetFormatMetadata(WebpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static WebpMetadata CloneWebpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance); + + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(GifFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static GifFrameMetadata CloneGifMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(GifFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static PngFrameMetadata ClonePngMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(PngFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static TiffFrameMetadata CloneTiffMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(TiffFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(WebpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static WebpFrameMetadata CloneWebpMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance); +} diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt new file mode 100644 index 000000000..e4db85ed5 --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt @@ -0,0 +1,77 @@ +<#@include file="_Formats.ttinclude" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +// +using SixLabors.ImageSharp.Metadata; +<# + foreach (string fmt in formats) + { +#> +using SixLabors.ImageSharp.Formats.<#= fmt #>; +<# + + } +#> + +namespace SixLabors.ImageSharp; + +/// +/// Extension methods for the and types. +/// +public static class ImageMetadataExtensions +{ +<# + foreach (string fmt in formats) + { +#> + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static <#= fmt #>Metadata Get<#= fmt #>Metadata(this ImageMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static <#= fmt #>Metadata Clone<#= fmt #>Metadata(this ImageMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance); + +<# + } +#> +<# + foreach (string fmt in frameFormats) + { +#> + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static <#= fmt #>FrameMetadata Get<#= fmt #>Metadata(this ImageFrameMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static <#= fmt #>FrameMetadata Clone<#= fmt #>Metadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance); +<# + } +#> +} diff --git a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude new file mode 100644 index 000000000..24ac66a70 --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude @@ -0,0 +1,24 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +<#+ + private static readonly string[] formats = new []{ + "Bmp", + "Gif", + "Jpeg", + "Pbm", + "Png", + "Qoi", + "Tga", + "Tiff", + "Webp", + }; + + private static readonly string[] frameFormats = new []{ + "Gif", + "Png", + "Tiff", + "Webp", + }; +#> diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 6096bd33e..d3c403471 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -52,6 +52,11 @@ + + True + True + ImageMetadataExtensions.tt + True True @@ -142,7 +147,7 @@ True PorterDuffFunctions.Generated.tt - + True True ImageExtensions.Save.tt @@ -150,6 +155,10 @@ + + ImageMetadataExtensions.cs + TextTemplatingFileGenerator + TextTemplatingFileGenerator Block8x8F.Generated.cs @@ -222,7 +231,7 @@ DefaultPixelBlenders.Generated.cs TextTemplatingFileGenerator - + TextTemplatingFileGenerator ImageExtensions.Save.cs diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 1c0330d5d..afda71879 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata; ///
    public sealed class ImageFrameMetadata : IDeepCloneable { - private readonly Dictionary formatMetadata = new(); + private readonly Dictionary formatMetadata = []; /// /// Initializes a new instance of the class. @@ -35,9 +35,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable { DebugGuard.NotNull(other, nameof(other)); - foreach (KeyValuePair meta in other.formatMetadata) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, (IFormatFrameMetadata)meta.Value.DeepClone()); } this.ExifProfile = other.ExifProfile?.DeepClone(); @@ -45,6 +45,10 @@ public sealed class ImageFrameMetadata : IDeepCloneable this.IptcProfile = other.IptcProfile?.DeepClone(); this.XmpProfile = other.XmpProfile?.DeepClone(); this.CicpProfile = other.CicpProfile?.DeepClone(); + + // NOTE: This clone is actually shallow but we share the same format + // instances for all images in the configuration. + this.DecodedImageFormat = other.DecodedImageFormat; } /// @@ -72,12 +76,19 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// public CicpProfile? CicpProfile { get; set; } + /// + /// Gets the original format, if any, the image was decode from. + /// + public IImageFormat? DecodedImageFormat { get; internal set; } + /// public ImageFrameMetadata DeepClone() => new(this); /// - /// Gets the metadata value associated with the specified key. This method will always return a result creating - /// a new instance and binding it to the frame metadata if none is found. + /// Gets the metadata value associated with the specified key.
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. ///
    /// The type of format metadata. /// The type of format frame metadata. @@ -87,43 +98,37 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// public TFormatFrameMetadata GetFormatMetadata(IImageFormat key) where TFormatMetadata : class - where TFormatFrameMetadata : class, IDeepCloneable + where TFormatFrameMetadata : class, IFormatFrameMetadata { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) + if (this.formatMetadata.TryGetValue(key, out IFormatFrameMetadata? meta)) { return (TFormatFrameMetadata)meta; } + // None found. Check if we have a decoded format to convert from. + if (this.DecodedImageFormat is not null + && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatFrameMetadata? decodedMetadata)) + { + return TFormatFrameMetadata.FromFormatConnectingFrameMetadata(decodedMetadata.ToFormatConnectingFrameMetadata()); + } + TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata(); this.formatMetadata[key] = newMeta; return newMeta; } /// - /// Gets the metadata value associated with the specified key. + /// Creates a new instance the metadata value associated with the specified key. + /// The instance is created from a clone generated via . /// - /// The type of format metadata. + /// The type of metadata. /// The type of format frame metadata. /// The key of the value to get. - /// - /// When this method returns, contains the metadata associated with the specified key, - /// if the key is found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// /// - /// if the frame metadata exists for the specified key; otherwise, . + /// The . /// - public bool TryGetFormatMetadata(IImageFormat key, out TFormatFrameMetadata? metadata) + public TFormatFrameMetadata CloneFormatMetadata(IImageFormat key) where TFormatMetadata : class - where TFormatFrameMetadata : class, IDeepCloneable - { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) - { - metadata = (TFormatFrameMetadata)meta; - return true; - } - - metadata = default; - return false; - } + where TFormatFrameMetadata : class, IFormatFrameMetadata + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); } diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index ceeea42cf..b521f8eee 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -33,7 +33,7 @@ public sealed class ImageMetadata : IDeepCloneable ///
    public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; - private readonly Dictionary formatMetadata = []; + private readonly Dictionary formatMetadata = []; private double horizontalResolution; private double verticalResolution; @@ -60,9 +60,9 @@ public sealed class ImageMetadata : IDeepCloneable this.VerticalResolution = other.VerticalResolution; this.ResolutionUnits = other.ResolutionUnits; - foreach (KeyValuePair meta in other.formatMetadata) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, (IFormatMetadata)meta.Value.DeepClone()); } this.ExifProfile = other.ExifProfile?.DeepClone(); @@ -170,7 +170,10 @@ public sealed class ImageMetadata : IDeepCloneable public IImageFormat? DecodedImageFormat { get; internal set; } /// - /// Gets the metadata value associated with the specified key. + /// Gets the metadata value associated with the specified key.
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. ///
    /// The type of metadata. /// The key of the value to get. @@ -178,47 +181,44 @@ public sealed class ImageMetadata : IDeepCloneable /// The . /// public TFormatMetadata GetFormatMetadata(IImageFormat key) - where TFormatMetadata : class, IDeepCloneable + where TFormatMetadata : class, IFormatMetadata { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) + // Check for existing metadata. + if (this.formatMetadata.TryGetValue(key, out IFormatMetadata? meta)) { return (TFormatMetadata)meta; } + // None found. Check if we have a decoded format to convert from. + if (this.DecodedImageFormat is not null + && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatMetadata? decodedMetadata)) + { + return TFormatMetadata.FromFormatConnectingMetadata(decodedMetadata.ToFormatConnectingMetadata()); + } + + // Fall back to a default instance. TFormatMetadata newMeta = key.CreateDefaultFormatMetadata(); this.formatMetadata[key] = newMeta; return newMeta; } /// - /// Gets the metadata value associated with the specified key. + /// Creates a new instance the metadata value associated with the specified key. + /// The instance is created from a clone generated via . /// - /// The type of format metadata. + /// The type of metadata. /// The key of the value to get. - /// - /// When this method returns, contains the metadata associated with the specified key, - /// if the key is found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// /// - /// if the frame metadata exists for the specified key; otherwise, . + /// The . /// - public bool TryGetFormatMetadata(IImageFormat key, out TFormatMetadata? metadata) - where TFormatMetadata : class, IDeepCloneable - { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) - { - metadata = (TFormatMetadata)meta; - return true; - } - - metadata = default; - return false; - } + public TFormatMetadata CloneFormatMetadata(IImageFormat key) + where TFormatMetadata : class, IFormatMetadata, new() + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); /// public ImageMetadata DeepClone() => new(this); + /// TODO: This should be called on save. /// /// Synchronizes the profiles with the current metadata. /// diff --git a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs index 7cd1284f4..7865b9900 100644 --- a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -31,13 +31,13 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// /// Gets the pixel color type. /// - public PixelColorType? ColorType { get; init; } + public PixelColorType ColorType { get; init; } /// /// Gets the pixel alpha transparency behavior. /// means unknown, unspecified. /// - public PixelAlphaRepresentation? AlphaRepresentation { get; init; } + public PixelAlphaRepresentation AlphaRepresentation { get; init; } /// /// Creates a new instance. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index f7a398ec1..64564ae1d 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -16,7 +16,7 @@ public class BmpMetadataTests { BmpMetadata meta = new() { BitsPerPixel = BmpBitsPerPixel.Bit24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; - BmpMetadata clone = (BmpMetadata)meta.DeepClone(); + BmpMetadata clone = meta.DeepClone(); clone.BitsPerPixel = BmpBitsPerPixel.Bit32; clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index aeb3bab7b..77ac51e8a 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -242,33 +242,11 @@ public class GifEncoderTests where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - - int count = 0; - foreach (ImageFrame frame in image.Frames) - { - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata _)) - { - count++; - } - } - provider.Utility.SaveTestOutputFile(image, extension: "gif"); using FileStream fs = File.OpenRead(provider.Utility.GetTestOutputFileName("gif")); using Image image2 = Image.Load(fs); - Assert.Equal(image.Frames.Count, image2.Frames.Count); - - count = 0; - foreach (ImageFrame frame in image2.Frames) - { - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata _)) - { - count++; - } - } - - Assert.Equal(image2.Frames.Count, count); } [Theory] @@ -358,10 +336,10 @@ public class GifEncoderTests switch (webpF.DisposalMethod) { - case WebpDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMode); break; - case WebpDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMode); break; diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs index a69d9d9ba..243a62d59 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Pbm; using static SixLabors.ImageSharp.Tests.TestImages.Pbm; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index f40e537a1..009327c17 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -526,10 +526,10 @@ public partial class PngEncoderTests switch (webpF.BlendMethod) { - case WebpBlendMethod.Source: + case FrameBlendMode.Source: Assert.Equal(FrameBlendMode.Source, pngF.BlendMode); break; - case WebpBlendMethod.Over: + case FrameBlendMode.Over: default: Assert.Equal(FrameBlendMode.Over, pngF.BlendMode); break; @@ -537,10 +537,10 @@ public partial class PngEncoderTests switch (webpF.DisposalMethod) { - case WebpDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(FrameDisposalMode.RestoreToBackground, pngF.DisposalMode); break; - case WebpDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(FrameDisposalMode.DoNotDispose, pngF.DisposalMode); break; diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 4d7236307..aff1b3594 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -98,13 +98,13 @@ public class WebpEncoderTests switch (gifF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: - Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, webpF.DisposalMethod); break; case FrameDisposalMode.RestoreToPrevious: case FrameDisposalMode.Unspecified: case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, webpF.DisposalMethod); break; } } @@ -147,22 +147,22 @@ public class WebpEncoderTests switch (pngF.BlendMode) { case FrameBlendMode.Source: - Assert.Equal(WebpBlendMethod.Source, webpF.BlendMethod); + Assert.Equal(FrameBlendMode.Source, webpF.BlendMethod); break; case FrameBlendMode.Over: default: - Assert.Equal(WebpBlendMethod.Over, webpF.BlendMethod); + Assert.Equal(FrameBlendMode.Over, webpF.BlendMethod); break; } switch (pngF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: - Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, webpF.DisposalMethod); break; case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, webpF.DisposalMethod); break; } } @@ -220,7 +220,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_q", quality); + string testOutputDetails = $"lossless_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -250,7 +250,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_m", method, "_q", quality); + string testOutputDetails = $"lossless_m{method}_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -290,7 +290,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("nearlossless", "_q", nearLosslessQuality); + string testOutputDetails = $"nearlossless_q{nearLosslessQuality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(nearLosslessQuality)); } @@ -314,7 +314,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_m", method); + string testOutputDetails = $"lossless_m{method}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -344,7 +344,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_q", quality); + string testOutputDetails = $"lossy_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); } @@ -364,7 +364,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_f", filterStrength); + string testOutputDetails = $"lossy_f{filterStrength}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(75)); } @@ -384,7 +384,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_sns", snsStrength); + string testOutputDetails = $"lossy_sns{snsStrength}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(75)); } @@ -414,7 +414,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_m", method, "_q", quality); + string testOutputDetails = $"lossy_m{method}_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); } From a06511f6d2a7fce35033a17e8e367a9d3c6fd838 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Jun 2024 17:13:28 +1000 Subject: [PATCH 192/220] Initialize pixel type info from decoder. --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 1 - src/ImageSharp/Formats/ImageDecoder.cs | 1 + .../Formats/Jpeg/JpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs | 10 +- src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 18 ++-- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 25 +---- src/ImageSharp/Formats/Png/PngMetadata.cs | 12 +-- src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs | 2 +- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 1 - .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Formats/Webp/WebpDecoderCore.cs | 1 - src/ImageSharp/ImageInfo.cs | 25 ++--- src/ImageSharp/Metadata/ImageMetadata.cs | 16 +++- .../Formats/Bmp/BmpDecoderTests.cs | 21 +++-- .../Formats/Bmp/BmpEncoderTests.cs | 18 ++-- .../Formats/Jpg/JpegDecoderTests.cs | 2 +- .../Formats/Png/PngDecoderTests.cs | 6 +- .../Formats/Qoi/QoiEncoderTests.cs | 4 +- .../Formats/Tga/TgaEncoderTests.cs | 18 ++-- .../Formats/Tiff/TiffDecoderBaseTester.cs | 2 +- .../Formats/Tiff/TiffEncoderBaseTester.cs | 3 +- .../Formats/WebP/WebpDecoderTests.cs | 2 +- .../Formats/WebP/WebpEncoderTests.cs | 9 +- .../Formats/WebP/YuvConversionTests.cs | 2 +- .../Image/ImageTests.ImageLoadTestBase.cs | 3 +- tests/ImageSharp.Tests/ImageInfoTests.cs | 25 +++-- tests/ImageSharp.Tests/TestFormat.cs | 14 +-- .../ReferenceCodecs/MagickReferenceDecoder.cs | 43 +++++++-- .../ReferenceCodecUtilities.cs | 94 +++++++++++++++++++ .../SystemDrawingReferenceDecoder.cs | 26 +++-- .../SystemDrawingReferenceEncoder.cs | 1 + .../TestUtilities/TestEnvironment.Formats.cs | 4 +- .../Tests/MagickReferenceCodecTests.cs | 15 ++- .../Tests/ReferenceDecoderBenchmarks.cs | 8 +- .../Tests/SystemDrawingReferenceCodecTests.cs | 2 +- .../Tests/TestImageProviderTests.cs | 26 +++-- 37 files changed, 306 insertions(+), 160 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index bed489752..9391d52df 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -208,7 +208,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ReadImageHeaders(stream, out _, out _); - return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), new(this.infoHeader.Width, this.infoHeader.Height), this.metadata); + return new ImageInfo(new(this.infoHeader.Width, this.infoHeader.Height), this.metadata); } /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index e110acc30..8bd3e3ba2 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -249,7 +249,6 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } return new ImageInfo( - new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), new(this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height), this.metadata, framesMetadata); diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index 4e79f79ef..42e265dae 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -307,6 +307,7 @@ public abstract class ImageDecoder : IImageDecoder if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format)) { info.Metadata.DecodedImageFormat = format; + info.PixelType = info.Metadata.GetDecodedPixelTypeInfo(); foreach (ImageFrameMetadata frame in info.FrameMetadataCollection) { diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 6028aab30..759cc700f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -226,7 +226,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.InitDerivedMetadataProperties(); Size pixelSize = this.Frame.PixelSize; - return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), new(pixelSize.Width, pixelSize.Height), this.Metadata); + return new ImageInfo(new(pixelSize.Width, pixelSize.Height), this.Metadata); } /// diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 3fe339865..590fb2fb0 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -69,7 +69,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals { this.ProcessHeader(stream); - var image = new Image(this.configuration, this.pixelSize.Width, this.pixelSize.Height, this.metadata); + Image image = new(this.configuration, this.pixelSize.Width, this.pixelSize.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); @@ -86,10 +86,9 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ProcessHeader(stream); - - // BlackAndWhite pixels are encoded into a byte. - int bitsPerPixel = this.componentType == PbmComponentType.Short ? 16 : 8; - return new ImageInfo(new PixelTypeInfo(bitsPerPixel), new(this.pixelSize.Width, this.pixelSize.Height), this.metadata); + return new ImageInfo( + new(this.pixelSize.Width, this.pixelSize.Height), + this.metadata); } /// @@ -97,6 +96,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals /// /// The input stream. /// An EOF marker has been read before the image has been decoded. + [MemberNotNull(nameof(metadata))] private void ProcessHeader(BufferedReadStream stream) { Span buffer = stackalloc byte[2]; diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 9b23aecac..fec4beda7 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -97,20 +97,20 @@ public class PbmMetadata : IFormatMetadata colorType = PixelColorType.Binary; info = PixelComponentInfo.Create(1, bpp, 1); break; - case PbmColorType.Grayscale: - bpp = 8; - colorType = PixelColorType.Luminance; - info = PixelComponentInfo.Create(1, bpp, 8); - break; case PbmColorType.Rgb: - bpp = 24; + bpp = this.ComponentType == PbmComponentType.Short ? 48 : 24; colorType = PixelColorType.RGB; - info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + info = this.ComponentType == PbmComponentType.Short + ? PixelComponentInfo.Create(3, bpp, 16, 16, 16) + : PixelComponentInfo.Create(3, bpp, 8, 8, 8); break; + case PbmColorType.Grayscale: default: - bpp = 8; + bpp = this.ComponentType == PbmComponentType.Short ? 16 : 8; colorType = PixelColorType.Luminance; - info = PixelComponentInfo.Create(1, bpp, 8); + info = this.ComponentType == PbmComponentType.Short + ? PixelComponentInfo.Create(1, bpp, bpp) + : PixelComponentInfo.Create(1, bpp, bpp); break; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index a4d13177d..49fab870b 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -515,7 +515,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); - return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata); + return new ImageInfo(new(this.header.Width, this.header.Height), metadata); } finally { @@ -680,29 +680,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.scanline = this.configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); } - /// - /// Calculates the correct number of bits per pixel for the given color type. - /// - /// The - private int CalculateBitsPerPixel() - { - switch (this.pngColorType) - { - case PngColorType.Grayscale: - case PngColorType.Palette: - return this.header.BitDepth; - case PngColorType.GrayscaleWithAlpha: - return this.header.BitDepth * 2; - case PngColorType.Rgb: - return this.header.BitDepth * 3; - case PngColorType.RgbWithAlpha: - return this.header.BitDepth * 4; - default: - PngThrowHelper.ThrowNotSupportedColor(); - return -1; - } - } - /// /// Calculates the correct number of bytes per pixel for the given color type. /// diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index beb4ca5ae..a7b3672ef 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -176,9 +176,9 @@ public class PngMetadata : IFormatMetadata break; case PngColorType.Grayscale: - bpp = 8; + bpp = (int)this.BitDepth; colorType = PixelColorType.Luminance; - info = PixelComponentInfo.Create(1, bpp, 8); + info = PixelComponentInfo.Create(1, bpp, bpp); break; case PngColorType.GrayscaleWithAlpha: @@ -200,15 +200,15 @@ public class PngMetadata : IFormatMetadata case PngColorType.Rgb: if (this.BitDepth == PngBitDepth.Bit16) { - bpp = 24; + bpp = 48; colorType = PixelColorType.RGB; - info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + info = PixelComponentInfo.Create(3, bpp, 16, 16, 16); break; } - bpp = 48; + bpp = 24; colorType = PixelColorType.RGB; - info = PixelComponentInfo.Create(3, bpp, 16, 16, 16); + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); break; case PngColorType.RgbWithAlpha: diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index 92974aade..5f408fe0b 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -73,7 +73,7 @@ internal class QoiDecoderCore : IImageDecoderInternals qoiMetadata.Channels = this.header.Channels; qoiMetadata.ColorSpace = this.header.ColorSpace; - return new ImageInfo(pixelType, size, metadata); + return new ImageInfo(size, metadata); } /// diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 47f7eb0f2..912b82e8e 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -646,7 +646,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { this.ReadFileHeader(stream); return new ImageInfo( - new PixelTypeInfo(this.fileHeader.PixelDepth), new(this.fileHeader.Width, this.fileHeader.Height), this.metadata); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 63dc62399..a31a29bdd 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -234,7 +234,7 @@ internal class TiffDecoderCore : IImageDecoderInternals int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); - return new ImageInfo(new PixelTypeInfo((int)framesMetadata[0].GetTiffMetadata().BitsPerPixel), new(width, height), metadata, framesMetadata); + return new ImageInfo(new(width, height), metadata, framesMetadata); } /// diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index c29742da5..8562ecb7d 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -144,7 +144,6 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable using (this.webImageInfo = this.ReadVp8Info(stream, metadata, true)) { return new ImageInfo( - new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), new Size((int)this.webImageInfo.Width, (int)this.webImageInfo.Height), metadata); } diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index c0d1f27ca..3a2e87017 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -14,40 +14,43 @@ public class ImageInfo /// /// Initializes a new instance of the class. /// - /// The pixel type information. /// The size of the image in px units. /// The image metadata. public ImageInfo( - PixelTypeInfo pixelType, Size size, - ImageMetadata? metadata) - : this(pixelType, size, metadata, null) + ImageMetadata metadata) + : this(size, metadata, null) { } /// /// Initializes a new instance of the class. /// - /// The pixel type information. /// The size of the image in px units. /// The image metadata. /// The collection of image frame metadata. public ImageInfo( - PixelTypeInfo pixelType, Size size, - ImageMetadata? metadata, + ImageMetadata metadata, IReadOnlyList? frameMetadataCollection) { - this.PixelType = pixelType; this.Size = size; - this.Metadata = metadata ?? new ImageMetadata(); - this.FrameMetadataCollection = frameMetadataCollection ?? Array.Empty(); + this.Metadata = metadata; + + // PixelTpe is normally set following decoding + // See ImageDecoder.SetDecoderFormat(Configuration configuration, ImageInfo info). + if (metadata.DecodedImageFormat is not null) + { + this.PixelType = metadata.GetDecodedPixelTypeInfo(); + } + + this.FrameMetadataCollection = frameMetadataCollection ?? []; } /// /// Gets information about the image pixels. /// - public PixelTypeInfo PixelType { get; } + public PixelTypeInfo PixelType { get; internal set; } /// /// Gets the image width in px units. diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index b521f8eee..72b560172 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Metadata; @@ -212,12 +213,25 @@ public sealed class ImageMetadata : IDeepCloneable /// The . /// public TFormatMetadata CloneFormatMetadata(IImageFormat key) - where TFormatMetadata : class, IFormatMetadata, new() + where TFormatMetadata : class, IFormatMetadata => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); /// public ImageMetadata DeepClone() => new(this); + internal PixelTypeInfo GetDecodedPixelTypeInfo() + { + // None found. Check if we have a decoded format to convert from. + if (this.DecodedImageFormat is not null + && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatMetadata? decodedMetadata)) + { + return decodedMetadata.GetPixelTypeInfo(); + } + + // This should never happen. + return default; + } + /// TODO: This should be called on save. /// /// Synchronizes the profiles with the current metadata. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index ffc2a7aef..94cfe85ee 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -4,6 +4,7 @@ using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -112,7 +113,7 @@ public class BmpDecoderTests { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder(BmpFormat.Instance)); } [Theory] @@ -219,7 +220,7 @@ public class BmpDecoderTests image.DebugSave(provider); if (TestEnvironment.IsWindows) { - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder(BmpFormat.Instance)); } } @@ -232,7 +233,7 @@ public class BmpDecoderTests BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -251,7 +252,7 @@ public class BmpDecoderTests BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -298,7 +299,7 @@ public class BmpDecoderTests { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -314,7 +315,7 @@ public class BmpDecoderTests // which should be remapped to 255 for RGBA32, but the magick decoder has a value of 191 set. // The total difference without the alpha channel is still: 0.0204% // Exporting the image as PNG with GIMP yields to the same result as the ImageSharp implementation. - image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), new MagickReferenceDecoder()); + image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), MagickReferenceDecoder.Png); } [Theory] @@ -327,7 +328,7 @@ public class BmpDecoderTests image.DebugSave(provider); // Do not validate. Reference files will fail validation. - image.CompareToOriginal(provider, new MagickReferenceDecoder(false)); + image.CompareToOriginal(provider, new MagickReferenceDecoder(PngFormat.Instance, false)); } [Theory] @@ -347,7 +348,7 @@ public class BmpDecoderTests { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -394,7 +395,7 @@ public class BmpDecoderTests { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -404,7 +405,7 @@ public class BmpDecoderTests { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index a0fbc210f..d68ec4755 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -20,11 +20,11 @@ public class BmpEncoderTests private static BmpEncoder BmpEncoder => new(); public static readonly TheoryData BitsPerPixel = - new() - { - BmpBitsPerPixel.Bit24, - BmpBitsPerPixel.Bit32 - }; + new() + { + BmpBitsPerPixel.Bit24, + BmpBitsPerPixel.Bit32 + }; public static readonly TheoryData RatioFiles = new() @@ -287,7 +287,7 @@ public class BmpEncoderTests provider, extension: "bmp", appendPixelTypeToFileName: false, - decoder: new MagickReferenceDecoder(false)); + decoder: new MagickReferenceDecoder(BmpFormat.Instance, false)); } [Theory] @@ -318,7 +318,7 @@ public class BmpEncoderTests provider, extension: "bmp", appendPixelTypeToFileName: false, - decoder: new MagickReferenceDecoder(false)); + decoder: new MagickReferenceDecoder(BmpFormat.Instance, false)); } [Theory] @@ -380,8 +380,8 @@ public class BmpEncoderTests { using Image image = provider.GetImage(); - using var reencodedStream = new MemoryStream(); - var encoder = new BmpEncoder + using MemoryStream reencodedStream = new(); + BmpEncoder encoder = new() { BitsPerPixel = bitsPerPixel, SupportTransparency = false, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 2fe428260..69f008e52 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg; [ValidateDisposedMemoryAllocations] public partial class JpegDecoderTests { - private static MagickReferenceDecoder ReferenceDecoder => new(); + private static MagickReferenceDecoder ReferenceDecoder => MagickReferenceDecoder.Jpeg; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.Bgr24 | PixelTypes.RgbaVector; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index a39e3a8b9..9bd78b8f2 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -485,7 +485,7 @@ public partial class PngDecoderTests if (compare) { // Magick cannot actually decode this image to compare. - image.CompareToOriginal(provider, new MagickReferenceDecoder(false)); + image.CompareToOriginal(provider, new MagickReferenceDecoder(PngFormat.Instance, false)); } } @@ -552,7 +552,7 @@ public partial class PngDecoderTests // We don't have another x-plat reference decoder that can be compared for this image. if (TestEnvironment.IsWindows) { - image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Instance); + image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Png); } }); Assert.Null(ex); @@ -614,7 +614,7 @@ public partial class PngDecoderTests // We don't have another x-plat reference decoder that can be compared for this image. if (TestEnvironment.IsWindows) { - image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Instance); + image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Png); } }); Assert.NotNull(ex); diff --git a/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs index d57b597b0..32ade4a1e 100644 --- a/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs @@ -24,7 +24,7 @@ public class QoiEncoderTests public static void Encode(TestImageProvider provider, QoiChannels channels, QoiColorSpace colorSpace) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(new MagickReferenceDecoder()); + using Image image = provider.GetImage(new MagickReferenceDecoder(QoiFormat.Instance)); using MemoryStream stream = new(); QoiEncoder encoder = new() { @@ -34,7 +34,7 @@ public class QoiEncoderTests image.Save(stream, encoder); stream.Position = 0; - using Image encodedImage = (Image)Image.Load(stream); + using Image encodedImage = Image.Load(stream); QoiMetadata qoiMetadata = encodedImage.Metadata.GetQoiMetadata(); ImageComparer.Exact.CompareImages(image, encodedImage); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index c9f93446f..615e0fc92 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -121,16 +121,14 @@ public class TgaEncoderTests where TPixel : unmanaged, IPixel { // The test image has alternating black and white pixels, which should make using always RLE data inefficient. - using (Image image = provider.GetImage()) - { - var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; - using (var memStream = new MemoryStream()) - { - image.Save(memStream, options); - byte[] imageBytes = memStream.ToArray(); - Assert.Equal(expectedBytes, imageBytes.Length); - } - } + using Image image = provider.GetImage(); + TgaEncoder options = new() { BitsPerPixel = TgaBitsPerPixel.Bit24, Compression = TgaCompression.RunLength }; + + using MemoryStream memStream = new(); + image.Save(memStream, options); + byte[] imageBytes = memStream.ToArray(); + + Assert.Equal(expectedBytes, imageBytes.Length); } // Run length encoded pixels should not exceed row boundaries. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs index 4acdf3e50..b574c7e55 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff; public abstract class TiffDecoderBaseTester { - protected static MagickReferenceDecoder ReferenceDecoder => new(); + protected static MagickReferenceDecoder ReferenceDecoder => new(TiffFormat.Instance); protected static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs index 0cff35217..1bf9f5a40 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff; [Trait("Format", "Tiff")] public abstract class TiffEncoderBaseTester { - protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); + protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(TiffFormat.Instance); protected static void TestStripLength( TestImageProvider provider, @@ -48,7 +48,6 @@ public abstract class TiffEncoderBaseTester Number[] stripByteCounts = exifProfileOutput.GetValue(ExifTag.StripByteCounts)?.Value; Assert.NotNull(stripByteCounts); Assert.True(stripByteCounts.Length > 1); - Assert.NotNull(outputMeta.BitsPerPixel); foreach (Number sz in stripByteCounts) { diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 99f4dee16..657ab2554 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [ValidateDisposedMemoryAllocations] public class WebpDecoderTests { - private static MagickReferenceDecoder ReferenceDecoder => new(); + private static MagickReferenceDecoder ReferenceDecoder => MagickReferenceDecoder.WebP; private static string TestImageLossyHorizontalFilterPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.AlphaCompressedHorizontalFilter); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index aff1b3594..5d09b3709 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -169,10 +169,11 @@ public class WebpEncoderTests } [Theory] - [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] // If its not a webp input image, it should default to lossy. + [WithFile(TestImages.Jpeg.Baseline.Jpeg410, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] // Input is lossy jpeg. + [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] // Input is lossless png. [WithFile(Lossless.NoTransform1, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] [WithFile(Lossy.BikeWithExif, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] - public void Encode_PreserveRatio(TestImageProvider provider, WebpFileFormatType expectedFormat) + public void Encode_PreserveEncodingType(TestImageProvider provider, WebpFileFormatType expectedFormat) where TPixel : unmanaged, IPixel { WebpEncoder options = new(); @@ -440,7 +441,7 @@ public class WebpEncoderTests "with_alpha", encoder, ImageComparer.Tolerant(0.04f), - referenceDecoder: new MagickReferenceDecoder()); + referenceDecoder: MagickReferenceDecoder.WebP); int encodedBytes = File.ReadAllBytes(encodedFile).Length; Assert.True(encodedBytes <= expectedFileSize, $"encoded bytes are {encodedBytes} and should be smaller then expected file size of {expectedFileSize}"); @@ -468,7 +469,7 @@ public class WebpEncoderTests "with_alpha_compressed", encoder, ImageComparer.Tolerant(0.04f), - referenceDecoder: new MagickReferenceDecoder()); + referenceDecoder: MagickReferenceDecoder.WebP); int encodedBytes = File.ReadAllBytes(encodedFile).Length; Assert.True(encodedBytes <= expectedFileSize, $"encoded bytes are {encodedBytes} and should be smaller then expected file size of {expectedFileSize}"); diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs index 3ae6601b1..f50bc8933 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [Trait("Format", "Webp")] public class YuvConversionTests { - private static MagickReferenceDecoder ReferenceDecoder => new(); + private static MagickReferenceDecoder ReferenceDecoder => MagickReferenceDecoder.WebP; private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Webp.Lossy.NoFilter06); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index 996310d8c..51e2a01a2 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -4,6 +4,7 @@ using Moq; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -57,7 +58,7 @@ public partial class ImageTests // TODO: Remove all this mocking. It's too complicated and we can now use fakes. this.localStreamReturnImageRgba32 = new Image(1, 1); this.localStreamReturnImageAgnostic = new Image(1, 1); - this.LocalImageInfo = new(new PixelTypeInfo(8), new(1, 1), new ImageMetadata()); + this.LocalImageInfo = new(new(1, 1), new ImageMetadata() { DecodedImageFormat = PngFormat.Instance }); this.localImageFormatMock = new Mock(); diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 576d14396..322b0af19 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; @@ -15,12 +15,14 @@ public class ImageInfoTests const int height = 60; Size size = new(width, height); Rectangle rectangle = new(0, 0, width, height); - PixelTypeInfo pixelType = new(8); - ImageMetadata meta = new(); - ImageInfo info = new(pixelType, size, meta); + // Initialize the metadata to match standard decoding behavior. + ImageMetadata meta = new() { DecodedImageFormat = PngFormat.Instance }; + meta.GetPngMetadata(); - Assert.Equal(pixelType, info.PixelType); + ImageInfo info = new(size, meta); + + Assert.NotEqual(default, info.PixelType); Assert.Equal(width, info.Width); Assert.Equal(height, info.Height); Assert.Equal(size, info.Size); @@ -35,13 +37,16 @@ public class ImageInfoTests const int height = 60; Size size = new(width, height); Rectangle rectangle = new(0, 0, width, height); - PixelTypeInfo pixelType = new(8); - ImageMetadata meta = new(); - IReadOnlyList frameMetadata = new List() { new() }; - ImageInfo info = new(pixelType, size, meta, frameMetadata); + // Initialize the metadata to match standard decoding behavior. + ImageMetadata meta = new() { DecodedImageFormat = PngFormat.Instance }; + meta.GetPngMetadata(); + + IReadOnlyList frameMetadata = [new()]; + + ImageInfo info = new(size, meta, frameMetadata); - Assert.Equal(pixelType, info.PixelType); + Assert.NotEqual(default, info.PixelType); Assert.Equal(width, info.Width); Assert.Equal(height, info.Height); Assert.Equal(size, info.Size); diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 10211f386..d30ce7846 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests; @@ -89,7 +90,7 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat { if (!this.sampleImages.ContainsKey(typeof(TPixel))) { - this.sampleImages.Add(typeof(TPixel), new Image(1, 1)); + this.sampleImages.Add(typeof(TPixel), ReferenceCodecUtilities.EnsureDecodedMetadata(new Image(1, 1), this)); } return (Image)this.sampleImages[typeof(TPixel)]; @@ -202,11 +203,12 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Image image = - this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - ImageFrameCollection m = image.Frames; - - return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); + using Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); + ImageMetadata metadata = image.Metadata; + return new(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 80b5536eb..f96dc19ee 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -5,6 +5,11 @@ using System.Runtime.InteropServices; using ImageMagick; using ImageMagick.Formats; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -13,19 +18,34 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; public class MagickReferenceDecoder : ImageDecoder { + private readonly IImageFormat imageFormat; private readonly bool validate; - public MagickReferenceDecoder() - : this(true) + public MagickReferenceDecoder(IImageFormat imageFormat) + : this(imageFormat, true) { } - public MagickReferenceDecoder(bool validate) => this.validate = validate; + public MagickReferenceDecoder(IImageFormat imageFormat, bool validate) + { + this.imageFormat = imageFormat; + this.validate = validate; + } + + public static MagickReferenceDecoder Png { get; } = new MagickReferenceDecoder(PngFormat.Instance); + + public static MagickReferenceDecoder Bmp { get; } = new MagickReferenceDecoder(BmpFormat.Instance); + + public static MagickReferenceDecoder Jpeg { get; } = new MagickReferenceDecoder(JpegFormat.Instance); - public static MagickReferenceDecoder Instance { get; } = new(); + public static MagickReferenceDecoder Tiff { get; } = new MagickReferenceDecoder(TiffFormat.Instance); + + public static MagickReferenceDecoder WebP { get; } = new MagickReferenceDecoder(WebpFormat.Instance); protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + ImageMetadata metadata = new(); + Configuration configuration = options.Configuration; BmpReadDefines bmpReadDefines = new() { @@ -44,7 +64,7 @@ public class MagickReferenceDecoder : ImageDecoder settings.SetDefines(pngReadDefines); using MagickImageCollection magickImageCollection = new(stream, settings); - List> framesList = new(); + List> framesList = []; foreach (IMagickImage magicFrame in magickImageCollection) { ImageFrame frame = new(configuration, magicFrame.Width, magicFrame.Height); @@ -61,6 +81,11 @@ public class MagickReferenceDecoder : ImageDecoder } else if (magicFrame.Depth is 16 or 14) { + if (this.imageFormat is PngFormat png) + { + metadata.GetPngMetadata().BitDepth = PngBitDepth.Bit16; + } + ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); Span bytes = MemoryMarshal.Cast(data.AsSpan()); FromRgba64Bytes(configuration, bytes, framePixels); @@ -71,7 +96,7 @@ public class MagickReferenceDecoder : ImageDecoder } } - return new Image(configuration, new ImageMetadata(), framesList); + return ReferenceCodecUtilities.EnsureDecodedMetadata(new(configuration, metadata, framesList), this.imageFormat); } protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) @@ -80,7 +105,11 @@ public class MagickReferenceDecoder : ImageDecoder protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(options, stream, cancellationToken); - return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); + ImageMetadata metadata = image.Metadata; + return new(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs new file mode 100644 index 000000000..e48116fed --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Pbm; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Qoi; +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +internal static class ReferenceCodecUtilities +{ + /// + /// Ensures that the metadata is properly initialized for reference and test encoders which cannot initialize + /// metadata in the same manner as our built in decoders. + /// + /// The type of pixel format. + /// The decoded image. + /// The image format + /// The format is unknown. + public static Image EnsureDecodedMetadata(Image image, IImageFormat format) + where TPixel : unmanaged, IPixel + { + if (image.Metadata.DecodedImageFormat is null) + { + image.Metadata.DecodedImageFormat = format; + } + + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.DecodedImageFormat = format; + } + + switch (format) + { + case BmpFormat: + image.Metadata.GetBmpMetadata(); + break; + case GifFormat: + image.Metadata.GetGifMetadata(); + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.GetGifMetadata(); + } + + break; + case JpegFormat: + image.Metadata.GetJpegMetadata(); + break; + case PbmFormat: + image.Metadata.GetPbmMetadata(); + break; + case PngFormat: + image.Metadata.GetPngMetadata(); + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.GetPngMetadata(); + } + + break; + case QoiFormat: + image.Metadata.GetQoiMetadata(); + break; + case TgaFormat: + image.Metadata.GetTgaMetadata(); + break; + case TiffFormat: + image.Metadata.GetTiffMetadata(); + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.GetTiffMetadata(); + } + + break; + case WebpFormat: + image.Metadata.GetWebpMetadata(); + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.GetWebpMetadata(); + } + + break; + } + + return image; + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index a3408bedb..14a655823 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -1,23 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#pragma warning disable CA1416 // Validate platform compatibility using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SDBitmap = System.Drawing.Bitmap; -using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; public class SystemDrawingReferenceDecoder : ImageDecoder { - public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); + private readonly IImageFormat imageFormat; + + public SystemDrawingReferenceDecoder(IImageFormat imageFormat) + => this.imageFormat = imageFormat; + + public static SystemDrawingReferenceDecoder Png { get; } = new SystemDrawingReferenceDecoder(PngFormat.Instance); + + public static SystemDrawingReferenceDecoder Bmp { get; } = new SystemDrawingReferenceDecoder(BmpFormat.Instance); protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using SDBitmap sourceBitmap = new(stream); - PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); - return new ImageInfo(pixelType, new(sourceBitmap.Width, sourceBitmap.Height), new ImageMetadata()); + using Image image = this.Decode(options, stream, cancellationToken); + ImageMetadata metadata = image.Metadata; + return new(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) @@ -42,7 +54,9 @@ public class SystemDrawingReferenceDecoder : ImageDecoder g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); } - return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); + return ReferenceCodecUtilities.EnsureDecodedMetadata( + SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap), + this.imageFormat); } protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs index d8dda2eea..0789ab263 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#pragma warning disable CA1416 // Validate platform compatibility using System.Drawing.Imaging; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 9508de246..e4bee955b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -71,13 +71,13 @@ public static partial class TestEnvironment // Magick codecs should work on all platforms cfg.ConfigureCodecs( PngFormat.Instance, - MagickReferenceDecoder.Instance, + MagickReferenceDecoder.Png, pngEncoder, new PngImageFormatDetector()); cfg.ConfigureCodecs( BmpFormat.Instance, - IsWindows ? SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, + IsWindows ? SystemDrawingReferenceDecoder.Bmp : MagickReferenceDecoder.Bmp, bmpEncoder, new BmpImageFormatDetector()); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index 81ea77b6b..b818e80b0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -7,14 +7,13 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit.Abstractions; -// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests; public class MagickReferenceCodecTests { public MagickReferenceCodecTests(ITestOutputHelper output) => this.Output = output; - private ITestOutputHelper Output { get; } + public ITestOutputHelper Output { get; } public const PixelTypes PixelTypesToTest32 = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; @@ -32,8 +31,8 @@ public class MagickReferenceCodecTests { string path = TestFile.GetInputFileFullPath(testImage); - var magickDecoder = new MagickReferenceDecoder(); - var sdDecoder = new SystemDrawingReferenceDecoder(); + MagickReferenceDecoder magickDecoder = MagickReferenceDecoder.Png; + SystemDrawingReferenceDecoder sdDecoder = SystemDrawingReferenceDecoder.Png; ImageComparer comparer = ImageComparer.Exact; @@ -64,11 +63,11 @@ public class MagickReferenceCodecTests { string path = TestFile.GetInputFileFullPath(testImage); - var magickDecoder = new MagickReferenceDecoder(); - var sdDecoder = new SystemDrawingReferenceDecoder(); + MagickReferenceDecoder magickDecoder = MagickReferenceDecoder.Png; + SystemDrawingReferenceDecoder sdDecoder = SystemDrawingReferenceDecoder.Png; - // 1020 == 4 * 255 (Equivalent to manhattan distance of 1+1+1+1=4 in Rgba32 space) - var comparer = ImageComparer.TolerantPercentage(1, 1020); + // 1020 == 4 * 255 (Equivalent to Manhattan distance of 1+1+1+1=4 in Rgba32 space) + ImageComparer comparer = ImageComparer.TolerantPercentage(1, 1020); using FileStream mStream = File.OpenRead(path); using FileStream sdStream = File.OpenRead(path); using Image mImage = magickDecoder.Decode(DecoderOptions.Default, mStream); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs index 3a3fcefdb..6e1eba28e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs @@ -47,7 +47,7 @@ public class ReferenceDecoderBenchmarks public void BenchmarkMagickPngDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - this.BenchmarkDecoderImpl(PngBenchmarkFiles, new MagickReferenceDecoder(), "Magick Decode Png"); + this.BenchmarkDecoderImpl(PngBenchmarkFiles, MagickReferenceDecoder.Png, "Magick Decode Png"); } [Theory(Skip = SkipBenchmarks)] @@ -55,7 +55,7 @@ public class ReferenceDecoderBenchmarks public void BenchmarkSystemDrawingPngDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - this.BenchmarkDecoderImpl(PngBenchmarkFiles, new SystemDrawingReferenceDecoder(), "System.Drawing Decode Png"); + this.BenchmarkDecoderImpl(PngBenchmarkFiles, SystemDrawingReferenceDecoder.Png, "System.Drawing Decode Png"); } [Theory(Skip = SkipBenchmarks)] @@ -63,7 +63,7 @@ public class ReferenceDecoderBenchmarks public void BenchmarkMagickBmpDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - this.BenchmarkDecoderImpl(BmpBenchmarkFiles, new MagickReferenceDecoder(), "Magick Decode Bmp"); + this.BenchmarkDecoderImpl(BmpBenchmarkFiles, MagickReferenceDecoder.Bmp, "Magick Decode Bmp"); } [Theory(Skip = SkipBenchmarks)] @@ -71,7 +71,7 @@ public class ReferenceDecoderBenchmarks public void BenchmarkSystemDrawingBmpDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - this.BenchmarkDecoderImpl(BmpBenchmarkFiles, new SystemDrawingReferenceDecoder(), "System.Drawing Decode Bmp"); + this.BenchmarkDecoderImpl(BmpBenchmarkFiles, SystemDrawingReferenceDecoder.Bmp, "System.Drawing Decode Bmp"); } private void BenchmarkDecoderImpl(IEnumerable testFiles, IImageDecoder decoder, string info, int times = DefaultExecutionCount) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs index a89feb3c3..460858379 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -93,7 +93,7 @@ public class SystemDrawingReferenceCodecTests { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); using FileStream stream = File.OpenRead(path); - using Image image = SystemDrawingReferenceDecoder.Instance.Decode(DecoderOptions.Default, stream); + using Image image = SystemDrawingReferenceDecoder.Png.Decode(DecoderOptions.Default, stream); image.DebugSave(dummyProvider); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 2f5291131..f33811206 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -3,9 +3,11 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit.Abstractions; // ReSharper disable InconsistentNaming @@ -25,7 +27,7 @@ public class TestImageProviderTests TestImageProvider.File(TestImages.Bmp.F) }; - public static string[] AllBmpFiles = { TestImages.Bmp.F, TestImages.Bmp.Bit8 }; + public static string[] AllBmpFiles = [TestImages.Bmp.F, TestImages.Bmp.Bit8]; public TestImageProviderTests(ITestOutputHelper output) => this.Output = output; @@ -80,9 +82,9 @@ public class TestImageProviderTests TestDecoder.DoTestThreadSafe( () => { - string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); + const string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); - var decoder = new TestDecoder(); + TestDecoder decoder = new(); decoder.InitCaller(testName); provider.GetImage(decoder); @@ -335,7 +337,7 @@ public class TestImageProviderTests { using (provider.GetImage()) { - var customConfiguration = Configuration.CreateDefaultInstance(); + Configuration customConfiguration = Configuration.CreateDefaultInstance(); provider.Configuration = customConfiguration; using Image image2 = provider.GetImage(); @@ -365,13 +367,17 @@ public class TestImageProviderTests protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); + ImageMetadata metadata = image.Metadata; + return new(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; - return new Image(42, 42); + return ReferenceCodecUtilities.EnsureDecodedMetadata(new Image(42, 42), PngFormat.Instance); } protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) @@ -408,13 +414,17 @@ public class TestImageProviderTests protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); + ImageMetadata metadata = image.Metadata; + return new(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } protected override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; - return new Image(42, 42); + return ReferenceCodecUtilities.EnsureDecodedMetadata(new Image(42, 42), PngFormat.Instance); } protected override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) From 8b5703c91670d3c716c3b23f0d4c946c4f0f1df3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Jun 2024 18:23:52 +1000 Subject: [PATCH 193/220] Synchronize profiles on save. --- src/ImageSharp/Formats/ImageEncoder.cs | 7 +++-- .../Formats/Jpeg/JpegEncoderCore.cs | 6 ---- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 1 - .../Formats/Webp/Lossless/Vp8LEncoder.cs | 2 -- .../Formats/Webp/Lossy/Vp8Encoder.cs | 2 -- src/ImageSharp/Image.cs | 22 ++++++++++++++ src/ImageSharp/Metadata/ImageFrameMetadata.cs | 5 ++++ src/ImageSharp/Metadata/ImageMetadata.cs | 11 ++++--- .../Metadata/Profiles/Exif/ExifProfile.cs | 11 +++++++ .../Metadata/ImageMetadataTests.cs | 29 +++++++++---------- 10 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/Formats/ImageEncoder.cs b/src/ImageSharp/Formats/ImageEncoder.cs index 4acd29e81..fdaa5c35d 100644 --- a/src/ImageSharp/Formats/ImageEncoder.cs +++ b/src/ImageSharp/Formats/ImageEncoder.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; @@ -42,6 +41,8 @@ public abstract class ImageEncoder : IImageEncoder private void EncodeWithSeekableStream(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + image.SynchronizeMetadata(); + Configuration configuration = image.Configuration; if (stream.CanSeek) { @@ -59,6 +60,8 @@ public abstract class ImageEncoder : IImageEncoder private async Task EncodeWithSeekableStreamAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + image.SynchronizeMetadata(); + Configuration configuration = image.Configuration; if (stream.CanSeek) { @@ -66,7 +69,7 @@ public abstract class ImageEncoder : IImageEncoder } else { - using ChunkedMemoryStream ms = new(configuration.MemoryAllocator); + await using ChunkedMemoryStream ms = new(configuration.MemoryAllocator); await DoEncodeAsync(ms); ms.Position = 0; await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 523d6b3ba..fbc928606 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -539,17 +539,11 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Temporary buffer. private void WriteProfiles(ImageMetadata metadata, Span buffer) { - if (metadata is null) - { - return; - } - // For compatibility, place the profiles in the following order: // - APP1 EXIF // - APP1 XMP // - APP2 ICC // - APP13 IPTC - metadata.SyncProfiles(); this.WriteExifProfile(metadata.ExifProfile, buffer); this.WriteXmpProfile(metadata.XmpProfile, buffer); this.WriteIccProfile(metadata.IccProfile, buffer); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index a5ab73988..9f2958272 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -776,7 +776,6 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable return; } - meta.SyncProfiles(); this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); } diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 58d007265..57f744e41 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -241,8 +241,6 @@ internal class Vp8LEncoder : IDisposable { // Write bytes from the bit-writer buffer to the stream. ImageMetadata metadata = image.Metadata; - metadata.SyncProfiles(); - ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index 7e1ef93cf..3ad72f7d0 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -315,8 +315,6 @@ internal class Vp8Encoder : IDisposable { // Write bytes from the bitwriter buffer to the stream. ImageMetadata metadata = image.Metadata; - metadata.SyncProfiles(); - ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 03a19a4be..d4f773abe 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -157,6 +157,28 @@ public abstract partial class Image : IDisposable, IConfigurationProvider public abstract Image CloneAs(Configuration configuration) where TPixel2 : unmanaged, IPixel; + /// + /// Synchronizes any embedded metadata profiles with the current image properties. + /// + public void SynchronizeMetadata() + { + this.Metadata.SynchronizeProfiles(); + foreach (ImageFrame frame in this.Frames) + { + frame.Metadata.SynchronizeProfiles(); + } + } + + /// + /// Synchronizes any embedded metadata profiles with the current image properties. + /// + /// A synchronization action to run in addition to the default process. + public void SynchronizeMetadata(Action action) + { + this.SynchronizeMetadata(); + action(this); + } + /// /// Update the size of the image after mutation. /// diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index afda71879..bb78f3b3e 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -131,4 +131,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable where TFormatMetadata : class where TFormatFrameMetadata : class, IFormatFrameMetadata => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); + + /// + /// Synchronizes the profiles with the current metadata. + /// + internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this); } diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 72b560172..3711da96d 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -219,6 +219,11 @@ public sealed class ImageMetadata : IDeepCloneable /// public ImageMetadata DeepClone() => new(this); + /// + /// Synchronizes the profiles with the current metadata. + /// + internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this); + internal PixelTypeInfo GetDecodedPixelTypeInfo() { // None found. Check if we have a decoded format to convert from. @@ -231,10 +236,4 @@ public sealed class ImageMetadata : IDeepCloneable // This should never happen. return default; } - - /// TODO: This should be called on save. - /// - /// Synchronizes the profiles with the current metadata. - /// - internal void SyncProfiles() => this.ExifProfile?.Sync(this); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index dd5792ae7..41d3c293b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -298,6 +298,17 @@ public sealed class ExifProfile : IDeepCloneable this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } + /// + /// Synchronizes the profiles with the specified metadata. + /// + /// The metadata. +#pragma warning disable CA1822, RCS1163, IDE0060 + internal void Sync(ImageFrameMetadata metadata) +#pragma warning restore IDE0060, RCS1163, CA1822 + { + // Nothing to do ....YET. + } + private void SyncResolution(ExifTag tag, double resolution) { if (!this.TryGetValue(tag, out IExifValue? value)) diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs index 330b70147..ae02c3d57 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs @@ -16,9 +16,9 @@ public class ImageMetadataTests [Fact] public void ConstructorImageMetadata() { - var metaData = new ImageMetadata(); + ImageMetadata metaData = new(); - var exifProfile = new ExifProfile(); + ExifProfile exifProfile = new(); metaData.ExifProfile = exifProfile; metaData.HorizontalResolution = 4; @@ -34,7 +34,7 @@ public class ImageMetadataTests [Fact] public void CloneIsDeep() { - var metaData = new ImageMetadata + ImageMetadata metaData = new() { ExifProfile = new ExifProfile(), HorizontalResolution = 4, @@ -53,7 +53,7 @@ public class ImageMetadataTests [Fact] public void HorizontalResolution() { - var metaData = new ImageMetadata(); + ImageMetadata metaData = new(); Assert.Equal(96, metaData.HorizontalResolution); metaData.HorizontalResolution = 0; @@ -69,7 +69,7 @@ public class ImageMetadataTests [Fact] public void VerticalResolution() { - var metaData = new ImageMetadata(); + ImageMetadata metaData = new(); Assert.Equal(96, metaData.VerticalResolution); metaData.VerticalResolution = 0; @@ -85,20 +85,19 @@ public class ImageMetadataTests [Fact] public void SyncProfiles() { - var exifProfile = new ExifProfile(); + ExifProfile exifProfile = new(); exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); - using (var image = new Image(1, 1)) - { - image.Metadata.ExifProfile = exifProfile; - image.Metadata.HorizontalResolution = 400; - image.Metadata.VerticalResolution = 500; + using Image image = new(1, 1); + image.Metadata.ExifProfile = exifProfile; + image.Metadata.HorizontalResolution = 400; + image.Metadata.VerticalResolution = 500; - image.Metadata.SyncProfiles(); + using MemoryStream memoryStream = new(); + image.SaveAsBmp(memoryStream); - Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble()); - Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble()); - } + Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble()); + Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble()); } } From 6beeba1575ed58dbde57332714c639a2d1451611 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 18 Jun 2024 21:41:08 +1000 Subject: [PATCH 194/220] Cleanup and fix issues --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 9 ++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 + src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 14 +++- src/ImageSharp/Formats/Cur/CurConstants.cs | 25 ++++-- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 12 ++- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 6 +- .../Formats/Cur/CurFrameMetadata.cs | 83 ++++++++----------- src/ImageSharp/Formats/Ico/IcoConstants.cs | 11 +-- src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 12 ++- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 4 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 6 +- .../Formats/Ico/IcoFrameMetadata.cs | 76 +++++++---------- src/ImageSharp/Formats/Icon/IconAssert.cs | 32 ------- .../Formats/Icon/IconDecoderCore.cs | 44 ++++++---- src/ImageSharp/Formats/Icon/IconDir.cs | 16 +++- src/ImageSharp/Formats/Icon/IconDirEntry.cs | 28 ++++++- .../Formats/Icon/IconEncoderCore.cs | 73 +++++++++++----- .../Formats/Icon/IconImageFormatDetector.cs | 6 +- 18 files changed, 263 insertions(+), 196 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 11c6aef29..c26536fd1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -1599,6 +1600,14 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } + if (palette.Length > 0) + { + Color[] colorTable = new Color[palette.Length / Unsafe.SizeOf()]; + ReadOnlySpan rgbTable = MemoryMarshal.Cast(palette); + Color.FromPixel(rgbTable, colorTable); + this.bmpMetadata.ColorTable = colorTable; + } + int skipAmount = 0; if (this.fileHeader.HasValue) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index f84e4f9c2..151da1828 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -109,6 +109,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.memoryAllocator = memoryAllocator; this.bitsPerPixel = encoder.BitsPerPixel; + + // TODO: Use a palette quantizer if supplied. this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = encoder.PixelSamplingStrategy; this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3; diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index a2ed1d21d..a50023b27 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp; @@ -23,6 +23,11 @@ public class BmpMetadata : IDeepCloneable { this.BitsPerPixel = other.BitsPerPixel; this.InfoHeaderType = other.InfoHeaderType; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } } /// @@ -35,8 +40,11 @@ public class BmpMetadata : IDeepCloneable /// public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + /// + /// Gets or sets the color table, if any. + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public IDeepCloneable DeepClone() => new BmpMetadata(this); - - // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/Cur/CurConstants.cs b/src/ImageSharp/Formats/Cur/CurConstants.cs index 6efd2817c..7abf4c812 100644 --- a/src/ImageSharp/Formats/Cur/CurConstants.cs +++ b/src/ImageSharp/Formats/Cur/CurConstants.cs @@ -9,20 +9,31 @@ namespace SixLabors.ImageSharp.Formats.Cur; internal static class CurConstants { /// - /// The list of mimetypes that equate to a ico. + /// The list of mime types that equate to a cur. /// /// /// See /// - public static readonly IEnumerable MimeTypes = new[] - { - "application/octet-stream", - }; + public static readonly IEnumerable MimeTypes = + [ + + // IANA-registered + "image/vnd.microsoft.icon", + + // ICO & CUR types used by Windows + "image/x-icon", + + // Erroneous types but have been used + "image/ico", + "image/icon", + "text/ico", + "application/ico", + ]; /// - /// The list of file extensions that equate to a ico. + /// The list of file extensions that equate to a cur. /// - public static readonly IEnumerable FileExtensions = new[] { "cur" }; + public static readonly IEnumerable FileExtensions = ["cur"]; public const uint FileHeader = 0x00_02_00_00; } diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 538f9a2c6..18ab8c75a 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -1,18 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurDecoderCore(DecoderOptions options) : IconDecoderCore(options) +internal sealed class CurDecoderCore : IconDecoderCore { - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + public CurDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); curFrameMetadata.FromIconDirEntry(entry); curFrameMetadata.Compression = compression; - curFrameMetadata.BitsPerPixel = bitsPerPixel; + curFrameMetadata.BmpBitsPerPixel = bitsPerPixel; } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index 3a7288b71..a6922d431 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -5,6 +5,10 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurEncoderCore() : IconEncoderCore(IconFileType.CUR) +internal sealed class CurEncoderCore : IconEncoderCore { + public CurEncoderCore() + : base(IconFileType.CUR) + { + } } diff --git a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index e8f3cfe8e..fc5cc5b2c 100644 --- a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; @@ -17,70 +18,48 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { } - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - /// hotspotX - /// hotspotY - public CurFrameMetadata(byte width, byte height, byte colorCount, ushort hotspotX, ushort hotspotY) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - this.HotspotX = hotspotX; - this.HotspotY = hotspotY; - } - - /// - public CurFrameMetadata(CurFrameMetadata metadata) + private CurFrameMetadata(CurFrameMetadata metadata) { - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; + this.Compression = metadata.Compression; this.HotspotX = metadata.HotspotX; this.HotspotY = metadata.HotspotY; - this.Compression = metadata.Compression; + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; } /// - /// Gets or sets icoFrameCompression. + /// Gets or sets the frame compressions format. /// public IconFrameCompression Compression { get; set; } /// - /// Gets or sets ColorCount field.
    - /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// Gets or sets the horizontal coordinates of the hotspot in number of pixels from the left. ///
    - // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } + public ushort HotspotX { get; set; } /// - /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + /// Gets or sets the vertical coordinates of the hotspot in number of pixels from the top. /// - public ushort HotspotX { get; set; } + public ushort HotspotY { get; set; } /// - /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. + /// Gets or sets the encoding width.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
    - public ushort HotspotY { get; set; } + public byte EncodingWidth { get; set; } /// - /// Gets or sets Height field.
    - /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + /// Gets or sets the encoding height.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
    public byte EncodingHeight { get; set; } /// - /// Gets or sets Width field.
    - /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// Gets or sets the number of bits per pixel.
    + /// Used when is ///
    - public byte EncodingWidth { get; set; } - - /// - public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; /// public CurFrameMetadata DeepClone() => new(this); @@ -88,21 +67,27 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); - internal void FromIconDirEntry(in IconDirEntry entry) + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; this.EncodingHeight = entry.Height; - this.ColorCount = entry.ColorCount; this.HotspotX = entry.Planes; this.HotspotY = entry.BitCount; } - internal IconDirEntry ToIconDirEntry() => new() + internal IconDirEntry ToIconDirEntry() { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = this.HotspotX, - BitCount = this.HotspotY, - }; + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + ? (byte)0 + : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); + + return new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + Planes = this.HotspotX, + BitCount = this.HotspotY, + ColorCount = colorCount + }; + } } diff --git a/src/ImageSharp/Formats/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Ico/IcoConstants.cs index 0b963a431..116579368 100644 --- a/src/ImageSharp/Formats/Ico/IcoConstants.cs +++ b/src/ImageSharp/Formats/Ico/IcoConstants.cs @@ -9,13 +9,14 @@ namespace SixLabors.ImageSharp.Formats.Ico; internal static class IcoConstants { /// - /// The list of mimetypes that equate to a ico. + /// The list of mime types that equate to a ico. /// /// /// See /// - public static readonly IEnumerable MimeTypes = new[] - { + public static readonly IEnumerable MimeTypes = + [ + // IANA-registered "image/vnd.microsoft.icon", @@ -27,12 +28,12 @@ internal static class IcoConstants "image/icon", "text/ico", "application/ico", - }; + ]; /// /// The list of file extensions that equate to a ico. /// - public static readonly IEnumerable FileExtensions = new[] { "ico" }; + public static readonly IEnumerable FileExtensions = ["ico"]; public const uint FileHeader = 0x00_01_00_00; } diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index 78cb0d961..e8629e35b 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -1,18 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoDecoderCore(DecoderOptions options) : IconDecoderCore(options) +internal sealed class IcoDecoderCore : IconDecoderCore { - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + public IcoDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); icoFrameMetadata.FromIconDirEntry(entry); icoFrameMetadata.Compression = compression; - icoFrameMetadata.BitsPerPixel = bitsPerPixel; + icoFrameMetadata.BmpBitsPerPixel = bitsPerPixel; } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs index 0668a7e23..298f93dec 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Ico; @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Ico; /// /// Image encoder for writing an image to a stream as a Windows Icon. /// -public sealed class IcoEncoder : QuantizingImageEncoder +public sealed class IcoEncoder : ImageEncoder { /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index 12ced58fd..ab3edfbd3 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -5,6 +5,10 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoEncoderCore() : IconEncoderCore(IconFileType.ICO) +internal sealed class IcoEncoderCore : IconEncoderCore { + public IcoEncoderCore() + : base(IconFileType.ICO) + { + } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 8d7eb17b5..82e4ce3b2 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -1,12 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; /// -/// IcoFrameMetadata. TODO: Remove base class and merge into this class. +/// Provides Ico specific metadata information for the image frame. /// public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { @@ -17,54 +18,36 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { } - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - public IcoFrameMetadata(byte width, byte height, byte colorCount) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - } - - /// - public IcoFrameMetadata(IcoFrameMetadata metadata) + private IcoFrameMetadata(IcoFrameMetadata metadata) { + this.Compression = metadata.Compression; this.EncodingWidth = metadata.EncodingWidth; this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; - this.Compression = metadata.Compression; + this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; } /// - /// Gets or sets icoFrameCompression. + /// Gets or sets the frame compressions format. /// public IconFrameCompression Compression { get; set; } /// - /// Gets or sets ColorCount field.
    - /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// Gets or sets the encoding width.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
    - // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } + public byte EncodingWidth { get; set; } /// - /// Gets or sets Height field.
    - /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + /// Gets or sets the encoding height.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
    public byte EncodingHeight { get; set; } /// - /// Gets or sets Width field.
    - /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// Gets or sets the number of bits per pixel.
    + /// Used when is ///
    - public byte EncodingWidth { get; set; } - - /// - public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; /// public IcoFrameMetadata DeepClone() => new(this); @@ -72,24 +55,29 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); - internal void FromIconDirEntry(in IconDirEntry entry) + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; this.EncodingHeight = entry.Height; - this.ColorCount = entry.ColorCount; } - internal IconDirEntry ToIconDirEntry() => new() + internal IconDirEntry ToIconDirEntry() { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = 1, - BitCount = this.Compression switch + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + ? (byte)0 + : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); + + return new() { - IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, - IconFrameCompression.Png => 32, - _ => throw new NotSupportedException($"Value: {this.Compression}"), - }, - }; + Width = this.EncodingWidth, + Height = this.EncodingHeight, + Planes = 1, + ColorCount = colorCount, + BitCount = this.Compression switch + { + IconFrameCompression.Bmp => (ushort)this.BmpBitsPerPixel, + IconFrameCompression.Png or _ => 32, + }, + }; + } } diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index 547b6a6eb..398a3e5f4 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -5,14 +5,6 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal class IconAssert { - internal static void CanSeek(Stream stream) - { - if (!stream.CanSeek) - { - throw new NotSupportedException("This stream cannot support seek"); - } - } - internal static int EndOfStream(int v, int length) { if (v != length) @@ -22,28 +14,4 @@ internal class IconAssert return v; } - - internal static long EndOfStream(long v, long length) - { - if (v != length) - { - throw new EndOfStreamException(); - } - - return v; - } - - internal static byte Is1ByteSize(int i) - { - if (i is 256) - { - return 0; - } - else if (i > byte.MaxValue) - { - throw new FormatException("Image size Too Large."); - } - - return (byte)i; - } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index a502b91c9..97d0aec6d 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -10,12 +10,15 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals +internal abstract class IconDecoderCore : IImageDecoderInternals { private IconDir fileHeader; private IconDirEntry[]? entries; - public DecoderOptions Options { get; } = options; + protected IconDecoderCore(DecoderOptions options) + => this.Options = options; + + public DecoderOptions Options { get; } public Size Dimensions { get; private set; } @@ -61,10 +64,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI } ImageMetadata metadata = new(); + BmpMetadata? bmpMetadata = null; PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { - BmpBitsPerPixel bitsPerPixel = default; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -80,12 +84,12 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI pngMetadata = x.Image.Metadata.GetPngMetadata(); } - // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngMetadata()); } else { - bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; + bmpMetadata = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = bmpMetadata.BitsPerPixel; } this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); @@ -96,6 +100,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI }).ToArray()); // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + if (pngMetadata != null) { result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); @@ -114,9 +123,10 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI ImageMetadata metadata = new(); ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; + int bpp = 0; for (int i = 0; i < frames.Length; i++) { - BmpBitsPerPixel bitsPerPixel = default; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -140,11 +150,13 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); frames[i] = new(); - if (isPng) + if (!isPng) { bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; } + bpp = Math.Max(bpp, (int)bitsPerPixel); + this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data @@ -152,7 +164,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } - return new(new(32), this.Dimensions, metadata, frames); + return new(new(bpp), this.Dimensions, metadata, frames); } protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); @@ -211,15 +223,13 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI GeneralOptions = this.Options, }); } - else + + return new BmpDecoderCore(new() { - return new BmpDecoderCore(new() - { - GeneralOptions = this.Options, - ProcessedAlphaMask = true, - SkipFileHeader = true, - UseDoubleHeight = true, - }); - } + GeneralOptions = this.Options, + ProcessedAlphaMask = true, + SkipFileHeader = true, + UseDoubleHeight = true, + }); } } diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index aa583ee1e..3e02538c8 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -9,8 +9,20 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal struct IconDir(ushort reserved, IconFileType type, ushort count) { public const int Size = 3 * sizeof(ushort); + + /// + /// Reserved. Must always be 0. + /// public ushort Reserved = reserved; + + /// + /// Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. + /// public IconFileType Type = type; + + /// + /// Specifies number of images in the file. + /// public ushort Count = count; public IconDir(IconFileType type) @@ -23,9 +35,9 @@ internal struct IconDir(ushort reserved, IconFileType type, ushort count) { } - public static IconDir Parse(in ReadOnlySpan data) + public static IconDir Parse(ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; - public unsafe void WriteTo(in Stream stream) + public readonly unsafe void WriteTo(Stream stream) => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index 7a8e09e37..eab15dd87 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -10,25 +10,51 @@ internal struct IconDirEntry { public const int Size = (4 * sizeof(byte)) + (2 * sizeof(ushort)) + (2 * sizeof(uint)); + /// + /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// public byte Width; + /// + /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.[ + /// public byte Height; + /// + /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// public byte ColorCount; + /// + /// Reserved. Should be 0. + /// public byte Reserved; + /// + /// In ICO format: Specifies color planes. Should be 0 or 1.
    + /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + ///
    public ushort Planes; + /// + /// In ICO format: Specifies bits per pixel.
    + /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. + ///
    public ushort BitCount; + /// + /// Specifies the size of the image's data in bytes + /// public uint BytesInRes; + /// + /// Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file. + /// public uint ImageOffset; public static IconDirEntry Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; - public unsafe void WriteTo(in Stream stream) + public readonly unsafe void WriteTo(in Stream stream) => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index b4d563d4b..5332d9a86 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -2,18 +2,23 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconEncoderCore(IconFileType iconFileType) - : IImageEncoderInternals +internal abstract class IconEncoderCore : IImageEncoderInternals { + private readonly IconFileType iconFileType; private IconDir fileHeader; + private EncodingFrameMetadata[]? entries; - private IconFrameMetadata[]? entries; + protected IconEncoderCore(IconFileType iconFileType) + => this.iconFileType = iconFileType; public void Encode( Image image, @@ -24,17 +29,18 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - IconAssert.CanSeek(stream); - // Stream may not at 0. long basePosition = stream.Position; this.InitHeader(image); + // We don't write the header and entries yet as we need to write the image data first. int dataOffset = IconDir.Size + (IconDirEntry.Size * this.entries.Length); _ = stream.Seek(dataOffset, SeekOrigin.Current); for (int i = 0; i < image.Frames.Count; i++) { + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. ImageFrame frame = image.Frames[i]; int width = this.entries[i].Entry.Width; if (width is 0) @@ -50,36 +56,54 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) this.entries[i].Entry.ImageOffset = (uint)stream.Position; - Image img = new(width, height); + // We crop the frame to the size specified in the metadata. + // TODO: we can optimize this by cropping the frame only if the new size is both required and different. + using Image encodingFrame = new(width, height); for (int y = 0; y < height; y++) { - frame.PixelBuffer.DangerousGetRowSpan(y)[..width].CopyTo(img.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); + frame.PixelBuffer.DangerousGetRowSpan(y)[..width] + .CopyTo(encodingFrame.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); } - QuantizingImageEncoder encoder = this.entries[i].Compression switch + ref EncodingFrameMetadata encodingMetadata = ref this.entries[i]; + + QuantizingImageEncoder encoder = encodingMetadata.Compression switch { - IconFrameCompression.Bmp => new Bmp.BmpEncoder() + IconFrameCompression.Bmp => new BmpEncoder() { + // We don't have access to the palette in the metadata so we need to quantize the image + // using a new one generated from the pixel data. + Quantizer = encodingMetadata.Entry.BitCount <= 8 + ? new WuQuantizer(new() + { + MaxColors = encodingMetadata.Entry.ColorCount + }) + : null, ProcessedAlphaMask = true, UseDoubleHeight = true, SkipFileHeader = true, SupportTransparency = false, - BitsPerPixel = iconFileType is IconFileType.ICO - ? (Bmp.BmpBitsPerPixel?)this.entries[i].Entry.BitCount - : Bmp.BmpBitsPerPixel.Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image + BitsPerPixel = encodingMetadata.BmpBitsPerPixel + }, + IconFrameCompression.Png => new PngEncoder() + { + // Only 32bit Png supported. + // https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473 + BitDepth = PngBitDepth.Bit8, + ColorType = PngColorType.RgbWithAlpha }, - IconFrameCompression.Png => new Png.PngEncoder(), _ => throw new NotSupportedException(), }; - encoder.Encode(img, stream); - this.entries[i].Entry.BytesInRes = (uint)stream.Position - this.entries[i].Entry.ImageOffset; + encoder.Encode(encodingFrame, stream); + encodingMetadata.Entry.BytesInRes = (uint)stream.Position - encodingMetadata.Entry.ImageOffset; } + // We now need to rewind the stream and write the header and the entries. long endPosition = stream.Position; _ = stream.Seek(basePosition, SeekOrigin.Begin); this.fileHeader.WriteTo(stream); - foreach (IconFrameMetadata frame in this.entries) + foreach (EncodingFrameMetadata frame in this.entries) { frame.Entry.WriteTo(stream); } @@ -88,33 +112,38 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) } [MemberNotNull(nameof(entries))] - private void InitHeader(in Image image) + private void InitHeader(Image image) { - this.fileHeader = new(iconFileType, (ushort)image.Frames.Count); - this.entries = iconFileType switch + this.fileHeader = new(this.iconFileType, (ushort)image.Frames.Count); + this.entries = this.iconFileType switch { IconFileType.ICO => image.Frames.Select(i => { IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); }).ToArray(), IconFileType.CUR => image.Frames.Select(i => { CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); }).ToArray(), _ => throw new NotSupportedException(), }; } - internal sealed class IconFrameMetadata(IconFrameCompression compression, IconDirEntry iconDirEntry) + internal sealed class EncodingFrameMetadata( + IconFrameCompression compression, + BmpBitsPerPixel bmpBitsPerPixel, + IconDirEntry iconDirEntry) { private IconDirEntry iconDirEntry = iconDirEntry; public IconFrameCompression Compression { get; set; } = compression; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = bmpBitsPerPixel; + public ref IconDirEntry Entry => ref this.iconDirEntry; } } diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index b66b6c79f..9e7d22de2 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -60,9 +60,7 @@ public class IconImageFormatDetector : IImageFormatDetector return true; } - else - { - return false; - } + + return false; } } From 7c0cd0baab036af0d1726976631beb9beef1487d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 18 Jun 2024 21:55:51 +1000 Subject: [PATCH 195/220] Merge separate assert file --- src/ImageSharp/Formats/Icon/IconAssert.cs | 17 ----------------- src/ImageSharp/Formats/Icon/IconDecoderCore.cs | 14 ++++++++++++-- src/ImageSharp/Formats/Icon/IconEncoderCore.cs | 3 ++- 3 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 src/ImageSharp/Formats/Icon/IconAssert.cs diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs deleted file mode 100644 index 398a3e5f4..000000000 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Icon; - -internal class IconAssert -{ - internal static int EndOfStream(int v, int length) - { - if (v != length) - { - throw new EndOfStreamException(); - } - - return v; - } -} diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 97d0aec6d..a0849fa61 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -175,14 +175,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span buffer = stackalloc byte[IconDirEntry.Size]; // ICONDIR - _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); + _ = CheckEndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); this.fileHeader = IconDir.Parse(buffer); // ICONDIRENTRY this.entries = new IconDirEntry[this.fileHeader.Count]; for (int i = 0; i < this.entries.Length; i++) { - _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); + _ = CheckEndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); this.entries[i] = IconDirEntry.Parse(buffer); } @@ -232,4 +232,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals UseDoubleHeight = true, }); } + + private static int CheckEndOfStream(int v, int length) + { + if (v != length) + { + throw new InvalidImageContentException("Not enough bytes to read icon header."); + } + + return v; + } } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 5332d9a86..eb07ab483 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -90,7 +90,8 @@ internal abstract class IconEncoderCore : IImageEncoderInternals // Only 32bit Png supported. // https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473 BitDepth = PngBitDepth.Bit8, - ColorType = PngColorType.RgbWithAlpha + ColorType = PngColorType.RgbWithAlpha, + CompressionLevel = PngCompressionLevel.BestCompression }, _ => throw new NotSupportedException(), }; From b222a6733e2a978a950b783a7a96979271001841 Mon Sep 17 00:00:00 2001 From: Poker Date: Tue, 18 Jun 2024 21:07:08 +0800 Subject: [PATCH 196/220] Add test files --- .../Formats/Icon/Cur/CurEncoderTests.cs | 9 ++ .../Formats/Icon/Ico/IcoDecoderTests.cs | 119 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 114 +++++++++++++++++ tests/Images/Input/Icon/1bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/1bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/1bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/1bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/1bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/1bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/1bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/1bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/1bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/1bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/1bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/1bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/1bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/1bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/1bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/1bpp_size_9x9.ico | 3 + .../Input/Icon/1bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/1bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/24bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/24bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/24bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/24bpp_size_1x1.ico | 3 + .../Images/Input/Icon/24bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/24bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/24bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/24bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/24bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/24bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/24bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/24bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/24bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/24bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/24bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/24bpp_size_9x9.ico | 3 + tests/Images/Input/Icon/24bpp_transp.ico | 3 + .../Input/Icon/24bpp_transp_not_square.ico | 3 + .../Input/Icon/24bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/32bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/32bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/32bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/32bpp_size_1x1.ico | 3 + .../Images/Input/Icon/32bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/32bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/32bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/32bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/32bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/32bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/32bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/32bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/32bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/32bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/32bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/32bpp_size_9x9.ico | 3 + tests/Images/Input/Icon/32bpp_transp.ico | 3 + .../Input/Icon/32bpp_transp_not_square.ico | 3 + .../Input/Icon/32bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/4bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/4bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/4bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/4bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/4bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/4bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/4bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/4bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/4bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/4bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/4bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/4bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/4bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/4bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/4bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/4bpp_size_9x9.ico | 3 + .../Input/Icon/4bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/4bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/8bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/8bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/8bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/8bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/8bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/8bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/8bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/8bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/8bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/8bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/8bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/8bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/8bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/8bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/8bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/8bpp_size_9x9.ico | 3 + .../Input/Icon/8bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/8bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/cur_fake.ico | 3 + tests/Images/Input/Icon/cur_real.cur | 3 + tests/Images/Input/Icon/ico_fake.cur | 3 + tests/Images/Input/Icon/invalid_RLE4.ico | 3 + tests/Images/Input/Icon/invalid_RLE8.ico | 3 + tests/Images/Input/Icon/invalid_all.ico | 3 + tests/Images/Input/Icon/invalid_bpp.ico | 3 + .../Images/Input/Icon/invalid_compression.ico | 3 + tests/Images/Input/Icon/invalid_png.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_a.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_b.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_c.ico | 3 + tests/Images/Input/Icon/multi_size_a.ico | 3 + tests/Images/Input/Icon/multi_size_b.ico | 3 + tests/Images/Input/Icon/multi_size_c.ico | 3 + tests/Images/Input/Icon/multi_size_d.ico | 3 + tests/Images/Input/Icon/multi_size_e.ico | 3 + tests/Images/Input/Icon/multi_size_f.ico | 3 + .../Input/Icon/multi_size_multi_bits_a.ico | 3 + .../Input/Icon/multi_size_multi_bits_b.ico | 3 + .../Input/Icon/multi_size_multi_bits_c.ico | 3 + .../Input/Icon/multi_size_multi_bits_d.ico | 3 + 117 files changed, 584 insertions(+) create mode 100644 tests/Images/Input/Icon/1bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/1bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/1bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/4bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/4bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/8bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/8bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/cur_fake.ico create mode 100644 tests/Images/Input/Icon/cur_real.cur create mode 100644 tests/Images/Input/Icon/ico_fake.cur create mode 100644 tests/Images/Input/Icon/invalid_RLE4.ico create mode 100644 tests/Images/Input/Icon/invalid_RLE8.ico create mode 100644 tests/Images/Input/Icon/invalid_all.ico create mode 100644 tests/Images/Input/Icon/invalid_bpp.ico create mode 100644 tests/Images/Input/Icon/invalid_compression.ico create mode 100644 tests/Images/Input/Icon/invalid_png.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_a.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_b.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_a.ico create mode 100644 tests/Images/Input/Icon/multi_size_b.ico create mode 100644 tests/Images/Input/Icon/multi_size_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_d.ico create mode 100644 tests/Images/Input/Icon/multi_size_e.ico create mode 100644 tests/Images/Input/Icon/multi_size_f.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_a.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_b.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_d.ico diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 9908786f1..58213397c 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -29,4 +30,12 @@ public class CurEncoderTests memStream.Seek(0, SeekOrigin.Begin); CurDecoder.Instance.Decode(new(), memStream); } + + [Theory] + [WithFile(CurFake, PixelTypes.Rgba32)] + [WithFile(CurReal, PixelTypes.Rgba32)] + public void CurDecoder_Decode2(TestImageProvider provider) + { + Debug.Assert(false); + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 56378653a..ab4ec0a3d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -21,4 +22,122 @@ public class IcoDecoderTests // TODO: Assert metadata, frame count, etc } + + [Theory] + [WithFile(Bpp1_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp1_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp1_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp4_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp4_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp8_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp8_transp_partial, PixelTypes.Rgba32)] + [WithFile(IcoFake, PixelTypes.Rgba32)] + [WithFile(Invalid_all, PixelTypes.Rgba32)] + [WithFile(Invalid_bpp, PixelTypes.Rgba32)] + [WithFile(Invalid_compression, PixelTypes.Rgba32)] + [WithFile(Invalid_png, PixelTypes.Rgba32)] + [WithFile(Invalid_RLE4, PixelTypes.Rgba32)] + [WithFile(Invalid_RLE8, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_a, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_b, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_a, PixelTypes.Rgba32)] + [WithFile(Multi_size_b, PixelTypes.Rgba32)] + [WithFile(Multi_size_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_d, PixelTypes.Rgba32)] + [WithFile(Multi_size_e, PixelTypes.Rgba32)] + [WithFile(Multi_size_f, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_a, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_b, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_d, PixelTypes.Rgba32)] + public void IcoDecoder_Decode2(TestImageProvider provider) + { + Debug.Assert(false); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8b76abca2..6db11c78f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1126,10 +1126,124 @@ public static class TestImages public static class Ico { public const string Flutter = "Icon/flutter.ico"; + public const string Bpp1_size_15x15 = "Icon/1bpp_size_15x15.ico"; + public const string Bpp1_size_16x16 = "Icon/1bpp_size_16x16.ico"; + public const string Bpp1_size_17x17 = "Icon/1bpp_size_17x17.ico"; + public const string Bpp1_size_1x1 = "Icon/1bpp_size_1x1.ico"; + public const string Bpp1_size_256x256 = "Icon/1bpp_size_256x256.ico"; + public const string Bpp1_size_2x2 = "Icon/1bpp_size_2x2.ico"; + public const string Bpp1_size_31x31 = "Icon/1bpp_size_31x31.ico"; + public const string Bpp1_size_32x32 = "Icon/1bpp_size_32x32.ico"; + public const string Bpp1_size_33x33 = "Icon/1bpp_size_33x33.ico"; + public const string Bpp1_size_3x3 = "Icon/1bpp_size_3x3.ico"; + public const string Bpp1_size_4x4 = "Icon/1bpp_size_4x4.ico"; + public const string Bpp1_size_5x5 = "Icon/1bpp_size_5x5.ico"; + public const string Bpp1_size_6x6 = "Icon/1bpp_size_6x6.ico"; + public const string Bpp1_size_7x7 = "Icon/1bpp_size_7x7.ico"; + public const string Bpp1_size_8x8 = "Icon/1bpp_size_8x8.ico"; + public const string Bpp1_size_9x9 = "Icon/1bpp_size_9x9.ico"; + public const string Bpp1_transp_not_square = "Icon/1bpp_transp_not_square.ico"; + public const string Bpp1_transp_partial = "Icon/1bpp_transp_partial.ico"; + public const string Bpp24_size_15x15 = "Icon/24bpp_size_15x15.ico"; + public const string Bpp24_size_16x16 = "Icon/24bpp_size_16x16.ico"; + public const string Bpp24_size_17x17 = "Icon/24bpp_size_17x17.ico"; + public const string Bpp24_size_1x1 = "Icon/24bpp_size_1x1.ico"; + public const string Bpp24_size_256x256 = "Icon/24bpp_size_256x256.ico"; + public const string Bpp24_size_2x2 = "Icon/24bpp_size_2x2.ico"; + public const string Bpp24_size_31x31 = "Icon/24bpp_size_31x31.ico"; + public const string Bpp24_size_32x32 = "Icon/24bpp_size_32x32.ico"; + public const string Bpp24_size_33x33 = "Icon/24bpp_size_33x33.ico"; + public const string Bpp24_size_3x3 = "Icon/24bpp_size_3x3.ico"; + public const string Bpp24_size_4x4 = "Icon/24bpp_size_4x4.ico"; + public const string Bpp24_size_5x5 = "Icon/24bpp_size_5x5.ico"; + public const string Bpp24_size_6x6 = "Icon/24bpp_size_6x6.ico"; + public const string Bpp24_size_7x7 = "Icon/24bpp_size_7x7.ico"; + public const string Bpp24_size_8x8 = "Icon/24bpp_size_8x8.ico"; + public const string Bpp24_size_9x9 = "Icon/24bpp_size_9x9.ico"; + public const string Bpp24_transp_not_square = "Icon/24bpp_transp_not_square.ico"; + public const string Bpp24_transp_partial = "Icon/24bpp_transp_partial.ico"; + public const string Bpp24_transp = "Icon/24bpp_transp.ico"; + public const string Bpp32_size_15x15 = "Icon/32bpp_size_15x15.ico"; + public const string Bpp32_size_16x16 = "Icon/32bpp_size_16x16.ico"; + public const string Bpp32_size_17x17 = "Icon/32bpp_size_17x17.ico"; + public const string Bpp32_size_1x1 = "Icon/32bpp_size_1x1.ico"; + public const string Bpp32_size_256x256 = "Icon/32bpp_size_256x256.ico"; + public const string Bpp32_size_2x2 = "Icon/32bpp_size_2x2.ico"; + public const string Bpp32_size_31x31 = "Icon/32bpp_size_31x31.ico"; + public const string Bpp32_size_32x32 = "Icon/32bpp_size_32x32.ico"; + public const string Bpp32_size_33x33 = "Icon/32bpp_size_33x33.ico"; + public const string Bpp32_size_3x3 = "Icon/32bpp_size_3x3.ico"; + public const string Bpp32_size_4x4 = "Icon/32bpp_size_4x4.ico"; + public const string Bpp32_size_5x5 = "Icon/32bpp_size_5x5.ico"; + public const string Bpp32_size_6x6 = "Icon/32bpp_size_6x6.ico"; + public const string Bpp32_size_7x7 = "Icon/32bpp_size_7x7.ico"; + public const string Bpp32_size_8x8 = "Icon/32bpp_size_8x8.ico"; + public const string Bpp32_size_9x9 = "Icon/32bpp_size_9x9.ico"; + public const string Bpp32_transp_not_square = "Icon/32bpp_transp_not_square.ico"; + public const string Bpp32_transp_partial = "Icon/32bpp_transp_partial.ico"; + public const string Bpp32_transp = "Icon/32bpp_transp.ico"; + public const string Bpp4_size_15x15 = "Icon/4bpp_size_15x15.ico"; + public const string Bpp4_size_16x16 = "Icon/4bpp_size_16x16.ico"; + public const string Bpp4_size_17x17 = "Icon/4bpp_size_17x17.ico"; + public const string Bpp4_size_1x1 = "Icon/4bpp_size_1x1.ico"; + public const string Bpp4_size_256x256 = "Icon/4bpp_size_256x256.ico"; + public const string Bpp4_size_2x2 = "Icon/4bpp_size_2x2.ico"; + public const string Bpp4_size_31x31 = "Icon/4bpp_size_31x31.ico"; + public const string Bpp4_size_32x32 = "Icon/4bpp_size_32x32.ico"; + public const string Bpp4_size_33x33 = "Icon/4bpp_size_33x33.ico"; + public const string Bpp4_size_3x3 = "Icon/4bpp_size_3x3.ico"; + public const string Bpp4_size_4x4 = "Icon/4bpp_size_4x4.ico"; + public const string Bpp4_size_5x5 = "Icon/4bpp_size_5x5.ico"; + public const string Bpp4_size_6x6 = "Icon/4bpp_size_6x6.ico"; + public const string Bpp4_size_7x7 = "Icon/4bpp_size_7x7.ico"; + public const string Bpp4_size_8x8 = "Icon/4bpp_size_8x8.ico"; + public const string Bpp4_size_9x9 = "Icon/4bpp_size_9x9.ico"; + public const string Bpp4_transp_not_square = "Icon/4bpp_transp_not_square.ico"; + public const string Bpp4_transp_partial = "Icon/4bpp_transp_partial.ico"; + public const string Bpp8_size_15x15 = "Icon/8bpp_size_15x15.ico"; + public const string Bpp8_size_16x16 = "Icon/8bpp_size_16x16.ico"; + public const string Bpp8_size_17x17 = "Icon/8bpp_size_17x17.ico"; + public const string Bpp8_size_1x1 = "Icon/8bpp_size_1x1.ico"; + public const string Bpp8_size_256x256 = "Icon/8bpp_size_256x256.ico"; + public const string Bpp8_size_2x2 = "Icon/8bpp_size_2x2.ico"; + public const string Bpp8_size_31x31 = "Icon/8bpp_size_31x31.ico"; + public const string Bpp8_size_32x32 = "Icon/8bpp_size_32x32.ico"; + public const string Bpp8_size_33x33 = "Icon/8bpp_size_33x33.ico"; + public const string Bpp8_size_3x3 = "Icon/8bpp_size_3x3.ico"; + public const string Bpp8_size_4x4 = "Icon/8bpp_size_4x4.ico"; + public const string Bpp8_size_5x5 = "Icon/8bpp_size_5x5.ico"; + public const string Bpp8_size_6x6 = "Icon/8bpp_size_6x6.ico"; + public const string Bpp8_size_7x7 = "Icon/8bpp_size_7x7.ico"; + public const string Bpp8_size_8x8 = "Icon/8bpp_size_8x8.ico"; + public const string Bpp8_size_9x9 = "Icon/8bpp_size_9x9.ico"; + public const string Bpp8_transp_not_square = "Icon/8bpp_transp_not_square.ico"; + public const string Bpp8_transp_partial = "Icon/8bpp_transp_partial.ico"; + public const string Invalid_all = "Icon/invalid_all.ico"; + public const string IcoFake = "Icon/ico_fake.cur"; + public const string Invalid_bpp = "Icon/invalid_bpp.ico"; + public const string Invalid_compression = "Icon/invalid_compression.ico"; + public const string Invalid_png = "Icon/invalid_png.ico"; + public const string Invalid_RLE4 = "Icon/invalid_RLE4.ico"; + public const string Invalid_RLE8 = "Icon/invalid_RLE8.ico"; + public const string Mixed_bmp_png_a = "Icon/mixed_bmp_png_a.ico"; + public const string Mixed_bmp_png_b = "Icon/mixed_bmp_png_b.ico"; + public const string Mixed_bmp_png_c = "Icon/mixed_bmp_png_c.ico"; + public const string Multi_size_a = "Icon/multi_size_a.ico"; + public const string Multi_size_b = "Icon/multi_size_b.ico"; + public const string Multi_size_c = "Icon/multi_size_c.ico"; + public const string Multi_size_d = "Icon/multi_size_d.ico"; + public const string Multi_size_e = "Icon/multi_size_e.ico"; + public const string Multi_size_f = "Icon/multi_size_f.ico"; + public const string Multi_size_multi_bits_a = "Icon/multi_size_multi_bits_a.ico"; + public const string Multi_size_multi_bits_b = "Icon/multi_size_multi_bits_b.ico"; + public const string Multi_size_multi_bits_c = "Icon/multi_size_multi_bits_c.ico"; + public const string Multi_size_multi_bits_d = "Icon/multi_size_multi_bits_d.ico"; } public static class Cur { public const string WindowsMouse = "Icon/aero_arrow.cur"; + public const string CurReal = "Icon/cur_real.cur"; + public const string CurFake = "Icon/cur_fake.ico"; } } diff --git a/tests/Images/Input/Icon/1bpp_size_15x15.ico b/tests/Images/Input/Icon/1bpp_size_15x15.ico new file mode 100644 index 000000000..39fc9c521 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:846dda605ee23bb641534b272fa57300eacd85038feea5dd1a3f6d4b543a935e +size 190 diff --git a/tests/Images/Input/Icon/1bpp_size_16x16.ico b/tests/Images/Input/Icon/1bpp_size_16x16.ico new file mode 100644 index 000000000..6179678bc --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:029d0438eda83d4d9e087cf79abe2d0234728c37f570de808355c0e79c71be17 +size 198 diff --git a/tests/Images/Input/Icon/1bpp_size_17x17.ico b/tests/Images/Input/Icon/1bpp_size_17x17.ico new file mode 100644 index 000000000..90138a08d --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8db024a49fdd91c9d5d1fc0c1d6f60991518a5776031f58cdafbdd3ed9e4f26b +size 206 diff --git a/tests/Images/Input/Icon/1bpp_size_1x1.ico b/tests/Images/Input/Icon/1bpp_size_1x1.ico new file mode 100644 index 000000000..1161a3f3c --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f13d1bbfa5b29bc386270cb492b705eded0825a9eb3a6341f4ea2b3dbe085cd1 +size 78 diff --git a/tests/Images/Input/Icon/1bpp_size_256x256.ico b/tests/Images/Input/Icon/1bpp_size_256x256.ico new file mode 100644 index 000000000..d6524a31f --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1aa37daf2fd65b424c5e13e94bed165328e624584cc5664d73bb4030a1e1f12 +size 16454 diff --git a/tests/Images/Input/Icon/1bpp_size_2x2.ico b/tests/Images/Input/Icon/1bpp_size_2x2.ico new file mode 100644 index 000000000..73394156a --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18be2a5384812de1bec70733fb0b283a159edc5d0bc03981de8fb3ccddb8911e +size 86 diff --git a/tests/Images/Input/Icon/1bpp_size_31x31.ico b/tests/Images/Input/Icon/1bpp_size_31x31.ico new file mode 100644 index 000000000..8dffe659f --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a4e7964e3ed5ca2a98929e2f903a6b969961410aab6a935a0c54fbe716d0c3 +size 318 diff --git a/tests/Images/Input/Icon/1bpp_size_32x32.ico b/tests/Images/Input/Icon/1bpp_size_32x32.ico new file mode 100644 index 000000000..e281eb378 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06a55f8219234e43beec6776fe15a7d6d4ad314deaf64df115ea45f2100e5283 +size 326 diff --git a/tests/Images/Input/Icon/1bpp_size_33x33.ico b/tests/Images/Input/Icon/1bpp_size_33x33.ico new file mode 100644 index 000000000..c5e4677d3 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82fa82f6b954515eace3cdd6d4681abd149b37354a7dee4f0d1966f516f27850 +size 598 diff --git a/tests/Images/Input/Icon/1bpp_size_3x3.ico b/tests/Images/Input/Icon/1bpp_size_3x3.ico new file mode 100644 index 000000000..89872b959 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ff0bf1c415925642d99691a9a9a6b92d9995447bc62ed80b9b0c6d4efdcd19b +size 94 diff --git a/tests/Images/Input/Icon/1bpp_size_4x4.ico b/tests/Images/Input/Icon/1bpp_size_4x4.ico new file mode 100644 index 000000000..1e47b4596 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5853ba73b06e0f2a0c71850011526419db3bd7c76e5b7b2f6b22f748ce919bf2 +size 102 diff --git a/tests/Images/Input/Icon/1bpp_size_5x5.ico b/tests/Images/Input/Icon/1bpp_size_5x5.ico new file mode 100644 index 000000000..5152c7575 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ecd253a6862ec9a9870c8a8a370528b28c22d23247dfc29e09fab65d95b9416d +size 110 diff --git a/tests/Images/Input/Icon/1bpp_size_6x6.ico b/tests/Images/Input/Icon/1bpp_size_6x6.ico new file mode 100644 index 000000000..a1d5c09c0 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a80c7f8d37dc3997bcd674a5af835cae802cacbf5d65020f0aaae67f70cfc31e +size 118 diff --git a/tests/Images/Input/Icon/1bpp_size_7x7.ico b/tests/Images/Input/Icon/1bpp_size_7x7.ico new file mode 100644 index 000000000..9c5a227e3 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00a26274e8563e6378f8bfa4d1aa9695030593a38791ca366cecd3949b0f52af +size 126 diff --git a/tests/Images/Input/Icon/1bpp_size_8x8.ico b/tests/Images/Input/Icon/1bpp_size_8x8.ico new file mode 100644 index 000000000..c019914ee --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78150aee2f5d5ccd2a172abfe11f1127efb950cb9173e22e380809afb2a94d3c +size 134 diff --git a/tests/Images/Input/Icon/1bpp_size_9x9.ico b/tests/Images/Input/Icon/1bpp_size_9x9.ico new file mode 100644 index 000000000..2f3fd28eb --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8cecf833b31208710c1dde1bed3322a95453468a3f4b39afdad66ac9bc5f86b +size 142 diff --git a/tests/Images/Input/Icon/1bpp_transp_not_square.ico b/tests/Images/Input/Icon/1bpp_transp_not_square.ico new file mode 100644 index 000000000..1c678ec40 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd9f41711b53d4e1e915ddb992522b97a981fbe3f4536826f0c66e2d6a3677fb +size 182 diff --git a/tests/Images/Input/Icon/1bpp_transp_partial.ico b/tests/Images/Input/Icon/1bpp_transp_partial.ico new file mode 100644 index 000000000..6365a53df --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cedcd221abf4b95115f9f8f34f15da456b09e7e56972cced24a99fa56bf8aca9 +size 326 diff --git a/tests/Images/Input/Icon/24bpp_size_15x15.ico b/tests/Images/Input/Icon/24bpp_size_15x15.ico new file mode 100644 index 000000000..f8697e2b5 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b506403852d936d14975ed9aba1c50ab3873cbbd81afcf38381f8e5e841fafd0 +size 842 diff --git a/tests/Images/Input/Icon/24bpp_size_16x16.ico b/tests/Images/Input/Icon/24bpp_size_16x16.ico new file mode 100644 index 000000000..e6de107d7 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bfa9de0f613e9e82e9037193c4124f87d05ac1390459e2f018da45c16200de6 +size 894 diff --git a/tests/Images/Input/Icon/24bpp_size_17x17.ico b/tests/Images/Input/Icon/24bpp_size_17x17.ico new file mode 100644 index 000000000..2c37ffa8b --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4cd2ad22e55000a706d365e0a3395f3e4d9a3933c00880d5f12903ac0aed60e +size 1014 diff --git a/tests/Images/Input/Icon/24bpp_size_1x1.ico b/tests/Images/Input/Icon/24bpp_size_1x1.ico new file mode 100644 index 000000000..f9137f61a --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b20e9a6479c3831b7af75d30bc909ce3046e45384bfd62d7a10ac6816c1c947 +size 70 diff --git a/tests/Images/Input/Icon/24bpp_size_256x256.ico b/tests/Images/Input/Icon/24bpp_size_256x256.ico new file mode 100644 index 000000000..08f928c8e --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1175641f39e68bd6cd38b8f64f02510b22c5a642afc7503d357b064163b3d37b +size 204862 diff --git a/tests/Images/Input/Icon/24bpp_size_2x2.ico b/tests/Images/Input/Icon/24bpp_size_2x2.ico new file mode 100644 index 000000000..d6d472a25 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72a1380a306b51ae37eabd3edeb0b134b0f8e8e120e6c64b6b08bd833a9c70a4 +size 86 diff --git a/tests/Images/Input/Icon/24bpp_size_31x31.ico b/tests/Images/Input/Icon/24bpp_size_31x31.ico new file mode 100644 index 000000000..5c585c582 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:574b0971908b44334f1f3be79e5b0ff336e74a8b9947b43a295d3a2b86733965 +size 3162 diff --git a/tests/Images/Input/Icon/24bpp_size_32x32.ico b/tests/Images/Input/Icon/24bpp_size_32x32.ico new file mode 100644 index 000000000..3663b8767 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa45880e5611436fc06de0603481d0a61044e52a1a053961fdda1139bb5660a6 +size 3262 diff --git a/tests/Images/Input/Icon/24bpp_size_33x33.ico b/tests/Images/Input/Icon/24bpp_size_33x33.ico new file mode 100644 index 000000000..4834b48c8 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8704a77d6264f6f104e23950099e3d3a4a577787e67c7c39d7925f9d0a347572 +size 3626 diff --git a/tests/Images/Input/Icon/24bpp_size_3x3.ico b/tests/Images/Input/Icon/24bpp_size_3x3.ico new file mode 100644 index 000000000..f2c11ccfe --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81229c27fbc684c0da99b1b04189046d5b9f531ee4111ccc43bde6404dce4f12 +size 110 diff --git a/tests/Images/Input/Icon/24bpp_size_4x4.ico b/tests/Images/Input/Icon/24bpp_size_4x4.ico new file mode 100644 index 000000000..2d7880a03 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7cb620eb83acbf20d308f5c6cb8b0246b8b156cdcc43e39f5809754fd4d5ddb +size 126 diff --git a/tests/Images/Input/Icon/24bpp_size_5x5.ico b/tests/Images/Input/Icon/24bpp_size_5x5.ico new file mode 100644 index 000000000..a98c85c19 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf1718e3d9695cdf2552e42219d1e3166ad5a94f53cbb44db4a7222e2a32f9a +size 162 diff --git a/tests/Images/Input/Icon/24bpp_size_6x6.ico b/tests/Images/Input/Icon/24bpp_size_6x6.ico new file mode 100644 index 000000000..5dd3c57c2 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e5f3a8f59e297cf67251a89558d4406c4c515c3e3ce7555df4a53ef5420fc38 +size 206 diff --git a/tests/Images/Input/Icon/24bpp_size_7x7.ico b/tests/Images/Input/Icon/24bpp_size_7x7.ico new file mode 100644 index 000000000..d9622629e --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ee1b5836c9f8c89e9b8c8873f1ad92f7555ed9706f4dc76345a727bf3e9f334 +size 258 diff --git a/tests/Images/Input/Icon/24bpp_size_8x8.ico b/tests/Images/Input/Icon/24bpp_size_8x8.ico new file mode 100644 index 000000000..39be58ce4 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b46ca32ddb84074d9140224738480eaa0a6c0dce2dbf2074625add1901c27117 +size 286 diff --git a/tests/Images/Input/Icon/24bpp_size_9x9.ico b/tests/Images/Input/Icon/24bpp_size_9x9.ico new file mode 100644 index 000000000..9e7873eaf --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7454d6332f4bdba929d610e4a8a232b1443d5a64119de4e69c00e0f03e55e237 +size 350 diff --git a/tests/Images/Input/Icon/24bpp_transp.ico b/tests/Images/Input/Icon/24bpp_transp.ico new file mode 100644 index 000000000..a64157a63 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb8ea41822350e5f40bac2aef19ec7a4c40561ce6637948b3fa6db7835c1fded +size 3262 diff --git a/tests/Images/Input/Icon/24bpp_transp_not_square.ico b/tests/Images/Input/Icon/24bpp_transp_not_square.ico new file mode 100644 index 000000000..5abf2ad66 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44b4c79ff497df0e99f55d68d82f59c0d7c2f4d5e9bb63bcc1b5910f4a2853db +size 1126 diff --git a/tests/Images/Input/Icon/24bpp_transp_partial.ico b/tests/Images/Input/Icon/24bpp_transp_partial.ico new file mode 100644 index 000000000..d1a37498b --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8d109413d7f699b92e9d6a5e2c52d2b5c747f2ca9ff31d326f8d4ec2fd5840f +size 3262 diff --git a/tests/Images/Input/Icon/32bpp_size_15x15.ico b/tests/Images/Input/Icon/32bpp_size_15x15.ico new file mode 100644 index 000000000..a7f94e94d --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02b5abd8e15ef2fba1a26cdbdf6e8f66abbbf9aa188404ef911a1d2d02b7b050 +size 1022 diff --git a/tests/Images/Input/Icon/32bpp_size_16x16.ico b/tests/Images/Input/Icon/32bpp_size_16x16.ico new file mode 100644 index 000000000..609a51826 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50bb07bd12f9b388ba6a3abbb815aaf1c800438e35ec48201269fa23342e5622 +size 1150 diff --git a/tests/Images/Input/Icon/32bpp_size_17x17.ico b/tests/Images/Input/Icon/32bpp_size_17x17.ico new file mode 100644 index 000000000..13a71140f --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bf4c701d38fc988186e3fcfee6e426ed5a84807b54b91e4ab8c1b12e0794746 +size 1286 diff --git a/tests/Images/Input/Icon/32bpp_size_1x1.ico b/tests/Images/Input/Icon/32bpp_size_1x1.ico new file mode 100644 index 000000000..3f449eefe --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f62be892b36609a57c34eb4784dfb6dc82698ecd15709898a6579ee4f21f668e +size 70 diff --git a/tests/Images/Input/Icon/32bpp_size_256x256.ico b/tests/Images/Input/Icon/32bpp_size_256x256.ico new file mode 100644 index 000000000..2229aee95 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b15f75adc54e70c4751cf9973854f225ef15b12bdaddb5ab00cb9edfd8b386b1 +size 270398 diff --git a/tests/Images/Input/Icon/32bpp_size_2x2.ico b/tests/Images/Input/Icon/32bpp_size_2x2.ico new file mode 100644 index 000000000..cbb64292b --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa859752136cdf055874ae71589f9585aaaeaef804b13ce192991793d9e57e57 +size 86 diff --git a/tests/Images/Input/Icon/32bpp_size_31x31.ico b/tests/Images/Input/Icon/32bpp_size_31x31.ico new file mode 100644 index 000000000..837e8f512 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5ecf1dc1fdd3dc6cb2fd90b49632b19260e73ad3a6624372d1e9eefc470ed6b +size 4030 diff --git a/tests/Images/Input/Icon/32bpp_size_32x32.ico b/tests/Images/Input/Icon/32bpp_size_32x32.ico new file mode 100644 index 000000000..b359d4d84 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:395ccdc596487ed63db4d893d03b6aa24743b37279d896196d8d38e7028415ea +size 4286 diff --git a/tests/Images/Input/Icon/32bpp_size_33x33.ico b/tests/Images/Input/Icon/32bpp_size_33x33.ico new file mode 100644 index 000000000..01df9ae7d --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e8b89c061abcf959b90149585cc34237aad19e1f67c162d62e5adbad4826970 +size 4682 diff --git a/tests/Images/Input/Icon/32bpp_size_3x3.ico b/tests/Images/Input/Icon/32bpp_size_3x3.ico new file mode 100644 index 000000000..8879d3f1f --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52b7772a9c8fae189abc3cf37d5d24bcdfe94077e6b1cf7be8cc7e0bc6f6bddf +size 110 diff --git a/tests/Images/Input/Icon/32bpp_size_4x4.ico b/tests/Images/Input/Icon/32bpp_size_4x4.ico new file mode 100644 index 000000000..d800b2198 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bb0831fab10fb0ff9a0b2b2ea137609a45a6ec3982d594878996eafc32836ad +size 142 diff --git a/tests/Images/Input/Icon/32bpp_size_5x5.ico b/tests/Images/Input/Icon/32bpp_size_5x5.ico new file mode 100644 index 000000000..710c38a23 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10e2ed5cbacc761f2d467c22a8d72dcc4086b9423c5487ba05826804c642730a +size 182 diff --git a/tests/Images/Input/Icon/32bpp_size_6x6.ico b/tests/Images/Input/Icon/32bpp_size_6x6.ico new file mode 100644 index 000000000..4223c1fc2 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab79a308d24592557b368f00566999d777e38de385eb6ebabc90d53ac723ae9 +size 230 diff --git a/tests/Images/Input/Icon/32bpp_size_7x7.ico b/tests/Images/Input/Icon/32bpp_size_7x7.ico new file mode 100644 index 000000000..1e321acb6 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3799164811b0284535fa90ca60f13e9c0754ca4e8f00d96a83951e91e748d760 +size 286 diff --git a/tests/Images/Input/Icon/32bpp_size_8x8.ico b/tests/Images/Input/Icon/32bpp_size_8x8.ico new file mode 100644 index 000000000..b44fe22d5 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a3187750c9313024f983fdfca270f5db2c5d730831adfce0fb19ddac25deecc +size 350 diff --git a/tests/Images/Input/Icon/32bpp_size_9x9.ico b/tests/Images/Input/Icon/32bpp_size_9x9.ico new file mode 100644 index 000000000..682b148ed --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab8bd47cc2d2ce0e9c1a5810b5ccbe3f46e35001114c18f34cbf51ff0566bf6 +size 422 diff --git a/tests/Images/Input/Icon/32bpp_transp.ico b/tests/Images/Input/Icon/32bpp_transp.ico new file mode 100644 index 000000000..592536290 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4f94718304fa41041b8cebad7b76762d410b366b46d620f5599b03a2fa7ba00 +size 4286 diff --git a/tests/Images/Input/Icon/32bpp_transp_not_square.ico b/tests/Images/Input/Icon/32bpp_transp_not_square.ico new file mode 100644 index 000000000..3a0bb3dd0 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e3c2061b64df989e8ae68aee993eba5e11d03e23f282f063cc3929ec3ef2b0c +size 1462 diff --git a/tests/Images/Input/Icon/32bpp_transp_partial.ico b/tests/Images/Input/Icon/32bpp_transp_partial.ico new file mode 100644 index 000000000..334a0b75c --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5bd34021ec302f203c39d038854607d9fe8bcd3133ea57b65ebc6da81aa8a4b +size 4286 diff --git a/tests/Images/Input/Icon/4bpp_size_15x15.ico b/tests/Images/Input/Icon/4bpp_size_15x15.ico new file mode 100644 index 000000000..ce67e54cc --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd1fd73648c1b7acc4aef4e0c4e6672ab7241e47cb52345c4459aa86185f4dfd +size 306 diff --git a/tests/Images/Input/Icon/4bpp_size_16x16.ico b/tests/Images/Input/Icon/4bpp_size_16x16.ico new file mode 100644 index 000000000..f26d88b36 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5f8547e5e6aae3a2f662fa266d0f78731d310fb051f99dce5693d6adcbfbb4f +size 318 diff --git a/tests/Images/Input/Icon/4bpp_size_17x17.ico b/tests/Images/Input/Icon/4bpp_size_17x17.ico new file mode 100644 index 000000000..aa44dd4f1 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fd0286a127371d4b40a5c30c6b221753a711231e386b636fcb07d2f1f92967f +size 398 diff --git a/tests/Images/Input/Icon/4bpp_size_1x1.ico b/tests/Images/Input/Icon/4bpp_size_1x1.ico new file mode 100644 index 000000000..7049b0b36 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:313f969c26d12cae6e778563d1c6c9df248c5d28ff657ad5054a280e06573106 +size 134 diff --git a/tests/Images/Input/Icon/4bpp_size_256x256.ico b/tests/Images/Input/Icon/4bpp_size_256x256.ico new file mode 100644 index 000000000..fa0740065 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcad369c1e56dd01b8644a5903ad25af19cc78047b5e0821546d1171a0ab31ff +size 41086 diff --git a/tests/Images/Input/Icon/4bpp_size_2x2.ico b/tests/Images/Input/Icon/4bpp_size_2x2.ico new file mode 100644 index 000000000..2b8d74afa --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11b1142401678412ce8718dd6e943fab05ec7974c7ae36286316e7f4d168f0f5 +size 142 diff --git a/tests/Images/Input/Icon/4bpp_size_31x31.ico b/tests/Images/Input/Icon/4bpp_size_31x31.ico new file mode 100644 index 000000000..86b71f36c --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b19f60f3441b268a19be0f99078d079c4eb416b882118071ad43ceff41ca40b +size 746 diff --git a/tests/Images/Input/Icon/4bpp_size_32x32.ico b/tests/Images/Input/Icon/4bpp_size_32x32.ico new file mode 100644 index 000000000..aa8fc0932 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f0863b486575dca2d5e3ce07da10cb30e039524f17026d9668d75bad780e833 +size 766 diff --git a/tests/Images/Input/Icon/4bpp_size_33x33.ico b/tests/Images/Input/Icon/4bpp_size_33x33.ico new file mode 100644 index 000000000..ad93685f2 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64b77f80fb48237a0f9cfbd808289e53ed7a70b107f190c93d76d32342829ccb +size 1050 diff --git a/tests/Images/Input/Icon/4bpp_size_3x3.ico b/tests/Images/Input/Icon/4bpp_size_3x3.ico new file mode 100644 index 000000000..781266a67 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8cb358defec66494d3be4eb01fd7e304edfd04435b4552d51769d0858659686 +size 150 diff --git a/tests/Images/Input/Icon/4bpp_size_4x4.ico b/tests/Images/Input/Icon/4bpp_size_4x4.ico new file mode 100644 index 000000000..ffe599149 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53083ae340dcc04d88acaf9d75a2dc68b23500c294eb3ed4e15e3fba86c5843f +size 158 diff --git a/tests/Images/Input/Icon/4bpp_size_5x5.ico b/tests/Images/Input/Icon/4bpp_size_5x5.ico new file mode 100644 index 000000000..70f2db036 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4f1401d87e285b18e6f818dfb00a3fb275e72e2fd4bce6652bfdd74bf9565f3 +size 166 diff --git a/tests/Images/Input/Icon/4bpp_size_6x6.ico b/tests/Images/Input/Icon/4bpp_size_6x6.ico new file mode 100644 index 000000000..230d8e85f --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3fa6bdd6125a9de6d3ddaafd5e62e0435b14c52f82239a6d36d44aaf5a49361 +size 174 diff --git a/tests/Images/Input/Icon/4bpp_size_7x7.ico b/tests/Images/Input/Icon/4bpp_size_7x7.ico new file mode 100644 index 000000000..7c4b9834b --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4025a4bd2dde619f950f69d38e042ae38d2a1840d7b00540c372546b5d8ccb0 +size 182 diff --git a/tests/Images/Input/Icon/4bpp_size_8x8.ico b/tests/Images/Input/Icon/4bpp_size_8x8.ico new file mode 100644 index 000000000..b1f3050bf --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:332563676808485aac8e2635baacad3f4d1ce5fc64c6be07daa25962f4fa1687 +size 190 diff --git a/tests/Images/Input/Icon/4bpp_size_9x9.ico b/tests/Images/Input/Icon/4bpp_size_9x9.ico new file mode 100644 index 000000000..fc7751086 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da5169395108194503853db2f431e0d02b98a191a1bf597c31fe269e7d84206a +size 234 diff --git a/tests/Images/Input/Icon/4bpp_transp_not_square.ico b/tests/Images/Input/Icon/4bpp_transp_not_square.ico new file mode 100644 index 000000000..6b2babe8e --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2dede473b7a427029cdb319ecf04aaf71cf8ce1d806efb65d7e3844ebd1703f9 +size 350 diff --git a/tests/Images/Input/Icon/4bpp_transp_partial.ico b/tests/Images/Input/Icon/4bpp_transp_partial.ico new file mode 100644 index 000000000..4394b9e43 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e0617f49eb057ab0346203e0cd04fd4e93de71053bbbfaa3c9655696b10a80d +size 766 diff --git a/tests/Images/Input/Icon/8bpp_size_15x15.ico b/tests/Images/Input/Icon/8bpp_size_15x15.ico new file mode 100644 index 000000000..086edb745 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:577c86aa8b78cca86885ed20b702b260150eacf738beb18da28c25bcb01cfdcd +size 1386 diff --git a/tests/Images/Input/Icon/8bpp_size_16x16.ico b/tests/Images/Input/Icon/8bpp_size_16x16.ico new file mode 100644 index 000000000..e09d74447 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9900b6783aac800836e19890f11fda1bf1d2138dee63403adbc79bf207a71dfd +size 1406 diff --git a/tests/Images/Input/Icon/8bpp_size_17x17.ico b/tests/Images/Input/Icon/8bpp_size_17x17.ico new file mode 100644 index 000000000..9a5684b4d --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b1c214eb0fd9f5b1f1da4ceeb98a3cc0d7b6b02d7ed6aaa5138078e48bf8613 +size 1494 diff --git a/tests/Images/Input/Icon/8bpp_size_1x1.ico b/tests/Images/Input/Icon/8bpp_size_1x1.ico new file mode 100644 index 000000000..20961847a --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:032c11a8e5aaa5f9beb997c83504233b5ad278d3a4e129cd384a4ca62950a998 +size 1094 diff --git a/tests/Images/Input/Icon/8bpp_size_256x256.ico b/tests/Images/Input/Icon/8bpp_size_256x256.ico new file mode 100644 index 000000000..59b0bb63b --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7aa8f4fc33c541f88318d7c36a5b5e964cb0470c541677790b49b85638c63fd6 +size 74814 diff --git a/tests/Images/Input/Icon/8bpp_size_2x2.ico b/tests/Images/Input/Icon/8bpp_size_2x2.ico new file mode 100644 index 000000000..bcbdf9c07 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e1c2b72b6ddf0cfedd9e20e5abf3ae3fd19066be811251bb9db404e6dabe279 +size 1102 diff --git a/tests/Images/Input/Icon/8bpp_size_31x31.ico b/tests/Images/Input/Icon/8bpp_size_31x31.ico new file mode 100644 index 000000000..7c320d183 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a588e6f1031c1c8f0d66b9eef770d01b3f79aaea4203d58ee07255c1c6ee258 +size 2238 diff --git a/tests/Images/Input/Icon/8bpp_size_32x32.ico b/tests/Images/Input/Icon/8bpp_size_32x32.ico new file mode 100644 index 000000000..af7581f35 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b00ab33cbb42e9f499844e5add713fcc83854cc2cb104d5c9bd04a456662099 +size 2238 diff --git a/tests/Images/Input/Icon/8bpp_size_33x33.ico b/tests/Images/Input/Icon/8bpp_size_33x33.ico new file mode 100644 index 000000000..3c8043fe6 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e356bb9200375f8fcbc5a0ed70353194a7b1432133e0c61f0b2f03bca3888080 +size 2538 diff --git a/tests/Images/Input/Icon/8bpp_size_3x3.ico b/tests/Images/Input/Icon/8bpp_size_3x3.ico new file mode 100644 index 000000000..075791998 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13cccd62e97bbe6cdd6ca5c4bc765794c7babd3638ff4d09b116a62c3c50be56 +size 1110 diff --git a/tests/Images/Input/Icon/8bpp_size_4x4.ico b/tests/Images/Input/Icon/8bpp_size_4x4.ico new file mode 100644 index 000000000..af95b287d --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4343544be11a37083219d99d2dfb4a16b02c309d54223897d6c71503414cc2ef +size 1118 diff --git a/tests/Images/Input/Icon/8bpp_size_5x5.ico b/tests/Images/Input/Icon/8bpp_size_5x5.ico new file mode 100644 index 000000000..2e4f80495 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7cfdf4a0d696573a4cb5291e3165c98444692f1d03a3cab1630df33c765ecd9 +size 1146 diff --git a/tests/Images/Input/Icon/8bpp_size_6x6.ico b/tests/Images/Input/Icon/8bpp_size_6x6.ico new file mode 100644 index 000000000..65ca70be2 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8d36eb593ee34d22e5ff99a08f2b177dbb92355f85210b2a9e53d7ce9e2cc6b +size 1158 diff --git a/tests/Images/Input/Icon/8bpp_size_7x7.ico b/tests/Images/Input/Icon/8bpp_size_7x7.ico new file mode 100644 index 000000000..e02e8fb37 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29050464e324a70bb8a41fb5732b96dbf3bcb36a74ca29775e1f37b07b9ee582 +size 1170 diff --git a/tests/Images/Input/Icon/8bpp_size_8x8.ico b/tests/Images/Input/Icon/8bpp_size_8x8.ico new file mode 100644 index 000000000..39be58ce4 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b46ca32ddb84074d9140224738480eaa0a6c0dce2dbf2074625add1901c27117 +size 286 diff --git a/tests/Images/Input/Icon/8bpp_size_9x9.ico b/tests/Images/Input/Icon/8bpp_size_9x9.ico new file mode 100644 index 000000000..8819490ae --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ddeaa63b1ec9d389c140499e2b5d2e911a57d8f37467696bb9e9ec3e60ec77b +size 1230 diff --git a/tests/Images/Input/Icon/8bpp_transp_not_square.ico b/tests/Images/Input/Icon/8bpp_transp_not_square.ico new file mode 100644 index 000000000..6b6bbdc9f --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04a255b4cd43ec7005a9a946c2b3dba57eba709b16be85ef77e553943d35f745 +size 1478 diff --git a/tests/Images/Input/Icon/8bpp_transp_partial.ico b/tests/Images/Input/Icon/8bpp_transp_partial.ico new file mode 100644 index 000000000..73cd34c73 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20abca3096b955bc91ed95d194ff3f856473d6d372b5bea0d8c75fd20a231a26 +size 2238 diff --git a/tests/Images/Input/Icon/cur_fake.ico b/tests/Images/Input/Icon/cur_fake.ico new file mode 100644 index 000000000..cad7542c8 --- /dev/null +++ b/tests/Images/Input/Icon/cur_fake.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6679016e7954e335cef537630d122cc3a7a05cb2f3ef32f72811d724b83d4c28 +size 4286 diff --git a/tests/Images/Input/Icon/cur_real.cur b/tests/Images/Input/Icon/cur_real.cur new file mode 100644 index 000000000..cad7542c8 --- /dev/null +++ b/tests/Images/Input/Icon/cur_real.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6679016e7954e335cef537630d122cc3a7a05cb2f3ef32f72811d724b83d4c28 +size 4286 diff --git a/tests/Images/Input/Icon/ico_fake.cur b/tests/Images/Input/Icon/ico_fake.cur new file mode 100644 index 000000000..5ab040538 --- /dev/null +++ b/tests/Images/Input/Icon/ico_fake.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d3a3185dcf0a2c3f5d3fe821474f6787c4de7cffe08db6bd730073ad94e7538 +size 4286 diff --git a/tests/Images/Input/Icon/invalid_RLE4.ico b/tests/Images/Input/Icon/invalid_RLE4.ico new file mode 100644 index 000000000..5d2e25fd3 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_RLE4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43c543a1c66608a89f0b187afa1526af4be4f7c94265897002b3a150328b964e +size 86 diff --git a/tests/Images/Input/Icon/invalid_RLE8.ico b/tests/Images/Input/Icon/invalid_RLE8.ico new file mode 100644 index 000000000..2ebefbf33 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_RLE8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b55b3f3f87d9d5801150ffd999c62623528a33d031ca8d6fe665be3328d8c94d +size 86 diff --git a/tests/Images/Input/Icon/invalid_all.ico b/tests/Images/Input/Icon/invalid_all.ico new file mode 100644 index 000000000..ca34a2578 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_all.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93c4f059ced481667315dd7b90e9c1beed0a42a08f3ff8e51e2388919fafa79a +size 283 diff --git a/tests/Images/Input/Icon/invalid_bpp.ico b/tests/Images/Input/Icon/invalid_bpp.ico new file mode 100644 index 000000000..913f780ed --- /dev/null +++ b/tests/Images/Input/Icon/invalid_bpp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:396bfd08531fc0eae8b5e364ef4c62035e8493a3f59286dedbca6f441d8e9690 +size 86 diff --git a/tests/Images/Input/Icon/invalid_compression.ico b/tests/Images/Input/Icon/invalid_compression.ico new file mode 100644 index 000000000..7e697d3d2 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_compression.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f051ed80db684748d2b2669c77b95068e209b2fe2917fa65f6218beef4dcead5 +size 830 diff --git a/tests/Images/Input/Icon/invalid_png.ico b/tests/Images/Input/Icon/invalid_png.ico new file mode 100644 index 000000000..cbd394fc6 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_png.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61bc9e74d8fd9f72c8ccaf9a3887c517e17c0a39d9d41acabc3699be545b9703 +size 901 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_a.ico b/tests/Images/Input/Icon/mixed_bmp_png_a.ico new file mode 100644 index 000000000..f35027255 --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47adfa4e36adf74ae49cfa481cb54cffe659c09d4b52765e973b6adc8cc31e97 +size 3653 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_b.ico b/tests/Images/Input/Icon/mixed_bmp_png_b.ico new file mode 100644 index 000000000..3efdcab74 --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96338768d8f9c90a6af94268dc55d94809a412c3164c381926b3759ffbf2df79 +size 45693 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_c.ico b/tests/Images/Input/Icon/mixed_bmp_png_c.ico new file mode 100644 index 000000000..65b504eef --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9be0358616581f45eddeaee474c0ce0be8a82279428f5ef1890b2fb6f0c0d27 +size 164189 diff --git a/tests/Images/Input/Icon/multi_size_a.ico b/tests/Images/Input/Icon/multi_size_a.ico new file mode 100644 index 000000000..c34fdc638 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dba75ec62f5785ce5d16c2c5c04637b18ccb164917092373b3d470326e7bc0c4 +size 17542 diff --git a/tests/Images/Input/Icon/multi_size_b.ico b/tests/Images/Input/Icon/multi_size_b.ico new file mode 100644 index 000000000..2065bd638 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d7ac6313ee263103f4ab6aa5147b1d85bf5ff792c0980189aac9bfab1288011 +size 99678 diff --git a/tests/Images/Input/Icon/multi_size_c.ico b/tests/Images/Input/Icon/multi_size_c.ico new file mode 100644 index 000000000..c6ee58297 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85e04b807e084bc9a0e1a65289e21ca1baac1e70c3a37fabbea7d69995945f08 +size 202850 diff --git a/tests/Images/Input/Icon/multi_size_d.ico b/tests/Images/Input/Icon/multi_size_d.ico new file mode 100644 index 000000000..3d9fc96fb --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_d.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a0ce27b63b386c4fdbf7835db738f0e98f274f5a112b7bd45c32dfab93952a0 +size 216804 diff --git a/tests/Images/Input/Icon/multi_size_e.ico b/tests/Images/Input/Icon/multi_size_e.ico new file mode 100644 index 000000000..8f2991acb --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_e.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:363ff3655e978ffe30a8cefec3bd4202a1ae0a22f3ab48e56362f56a31fb349f +size 372526 diff --git a/tests/Images/Input/Icon/multi_size_f.ico b/tests/Images/Input/Icon/multi_size_f.ico new file mode 100644 index 000000000..99948cf1e --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_f.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04701ce87eb82280a4e53d816a0ac3ee91ebc28b1959641bddb90787015ff4a8 +size 3084 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_a.ico b/tests/Images/Input/Icon/multi_size_multi_bits_a.ico new file mode 100644 index 000000000..12b2bf66c --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1561795509c9e8dbf0633db9b4c242e72e0ebe4ae9a7718328e96b6b273c3ca +size 4710 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_b.ico b/tests/Images/Input/Icon/multi_size_multi_bits_b.ico new file mode 100644 index 000000000..599168aea --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fe15c1a8ca4bae0ad588863418af960fb62def2db4abfcae594de4fc1b2304a +size 31134 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_c.ico b/tests/Images/Input/Icon/multi_size_multi_bits_c.ico new file mode 100644 index 000000000..701b574dc --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5afa3da7d278c1d2272581971117408c38ec5fe3aaac17e5234f7a8012fd9e2 +size 72513 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_d.ico b/tests/Images/Input/Icon/multi_size_multi_bits_d.ico new file mode 100644 index 000000000..271ec92a5 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_d.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0754e570ab3a2ce81759aa206fc6e8780fb1024fb20e60dbdb329ee4c0c1831 +size 293950 From bad83f9a9018f0a2021e5d06b360b8484be16b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:29:23 +0800 Subject: [PATCH 197/220] Update identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 222 +++++++++--------- tests/ImageSharp.Tests/TestImages.cs | 222 +++++++++--------- 2 files changed, 222 insertions(+), 222 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index ab4ec0a3d..8ea7cf6d4 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -24,118 +24,118 @@ public class IcoDecoderTests } [Theory] - [WithFile(Bpp1_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp1_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp1_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp4_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp4_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp8_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp8_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp1Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp1Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp1Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp1Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp1Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp1Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp1Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp1Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp1Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp1Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp1Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp1Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp1Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp1Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp1Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp1Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp1TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp1TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp24Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp24Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp24Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp24Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp24Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp24Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp24Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp24Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp24Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp24Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp24Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp24Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp24Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp24Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp24Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp24Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp24TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp24TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp24Transp, PixelTypes.Rgba32)] + [WithFile(Bpp32Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp32Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp32Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp32Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp32Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp32Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp32Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp32Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp32Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp32Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp32Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp32Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp32Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp32Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp32Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp32Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp32TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp32TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp32Transp, PixelTypes.Rgba32)] + [WithFile(Bpp4Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp4Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp4Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp4Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp4Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp4Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp4Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp4Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp4Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp4Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp4Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp4Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp4Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp4Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp4Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp4Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp4TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp4TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp8Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp8Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp8Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp8Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp8Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp8Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp8Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp8Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp8Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp8Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp8Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp8Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp8Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp8Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] + [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(IcoFake, PixelTypes.Rgba32)] - [WithFile(Invalid_all, PixelTypes.Rgba32)] - [WithFile(Invalid_bpp, PixelTypes.Rgba32)] - [WithFile(Invalid_compression, PixelTypes.Rgba32)] - [WithFile(Invalid_png, PixelTypes.Rgba32)] - [WithFile(Invalid_RLE4, PixelTypes.Rgba32)] - [WithFile(Invalid_RLE8, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_a, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_b, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_a, PixelTypes.Rgba32)] - [WithFile(Multi_size_b, PixelTypes.Rgba32)] - [WithFile(Multi_size_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_d, PixelTypes.Rgba32)] - [WithFile(Multi_size_e, PixelTypes.Rgba32)] - [WithFile(Multi_size_f, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_a, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_b, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_d, PixelTypes.Rgba32)] + [WithFile(InvalidBpp, PixelTypes.Rgba32)] + [WithFile(InvalidCompression, PixelTypes.Rgba32)] + [WithFile(InvalidPng, PixelTypes.Rgba32)] + [WithFile(InvalidRLE4, PixelTypes.Rgba32)] + [WithFile(InvalidRLE8, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngC, PixelTypes.Rgba32)] + [WithFile(MultiSizeA, PixelTypes.Rgba32)] + [WithFile(MultiSizeB, PixelTypes.Rgba32)] + [WithFile(MultiSizeC, PixelTypes.Rgba32)] + [WithFile(MultiSizeD, PixelTypes.Rgba32)] + [WithFile(MultiSizeE, PixelTypes.Rgba32)] + [WithFile(MultiSizeF, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsA, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsB, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsC, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsD, PixelTypes.Rgba32)] public void IcoDecoder_Decode2(TestImageProvider provider) { Debug.Assert(false); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6db11c78f..fcb32eae6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1126,118 +1126,118 @@ public static class TestImages public static class Ico { public const string Flutter = "Icon/flutter.ico"; - public const string Bpp1_size_15x15 = "Icon/1bpp_size_15x15.ico"; - public const string Bpp1_size_16x16 = "Icon/1bpp_size_16x16.ico"; - public const string Bpp1_size_17x17 = "Icon/1bpp_size_17x17.ico"; - public const string Bpp1_size_1x1 = "Icon/1bpp_size_1x1.ico"; - public const string Bpp1_size_256x256 = "Icon/1bpp_size_256x256.ico"; - public const string Bpp1_size_2x2 = "Icon/1bpp_size_2x2.ico"; - public const string Bpp1_size_31x31 = "Icon/1bpp_size_31x31.ico"; - public const string Bpp1_size_32x32 = "Icon/1bpp_size_32x32.ico"; - public const string Bpp1_size_33x33 = "Icon/1bpp_size_33x33.ico"; - public const string Bpp1_size_3x3 = "Icon/1bpp_size_3x3.ico"; - public const string Bpp1_size_4x4 = "Icon/1bpp_size_4x4.ico"; - public const string Bpp1_size_5x5 = "Icon/1bpp_size_5x5.ico"; - public const string Bpp1_size_6x6 = "Icon/1bpp_size_6x6.ico"; - public const string Bpp1_size_7x7 = "Icon/1bpp_size_7x7.ico"; - public const string Bpp1_size_8x8 = "Icon/1bpp_size_8x8.ico"; - public const string Bpp1_size_9x9 = "Icon/1bpp_size_9x9.ico"; - public const string Bpp1_transp_not_square = "Icon/1bpp_transp_not_square.ico"; - public const string Bpp1_transp_partial = "Icon/1bpp_transp_partial.ico"; - public const string Bpp24_size_15x15 = "Icon/24bpp_size_15x15.ico"; - public const string Bpp24_size_16x16 = "Icon/24bpp_size_16x16.ico"; - public const string Bpp24_size_17x17 = "Icon/24bpp_size_17x17.ico"; - public const string Bpp24_size_1x1 = "Icon/24bpp_size_1x1.ico"; - public const string Bpp24_size_256x256 = "Icon/24bpp_size_256x256.ico"; - public const string Bpp24_size_2x2 = "Icon/24bpp_size_2x2.ico"; - public const string Bpp24_size_31x31 = "Icon/24bpp_size_31x31.ico"; - public const string Bpp24_size_32x32 = "Icon/24bpp_size_32x32.ico"; - public const string Bpp24_size_33x33 = "Icon/24bpp_size_33x33.ico"; - public const string Bpp24_size_3x3 = "Icon/24bpp_size_3x3.ico"; - public const string Bpp24_size_4x4 = "Icon/24bpp_size_4x4.ico"; - public const string Bpp24_size_5x5 = "Icon/24bpp_size_5x5.ico"; - public const string Bpp24_size_6x6 = "Icon/24bpp_size_6x6.ico"; - public const string Bpp24_size_7x7 = "Icon/24bpp_size_7x7.ico"; - public const string Bpp24_size_8x8 = "Icon/24bpp_size_8x8.ico"; - public const string Bpp24_size_9x9 = "Icon/24bpp_size_9x9.ico"; - public const string Bpp24_transp_not_square = "Icon/24bpp_transp_not_square.ico"; - public const string Bpp24_transp_partial = "Icon/24bpp_transp_partial.ico"; - public const string Bpp24_transp = "Icon/24bpp_transp.ico"; - public const string Bpp32_size_15x15 = "Icon/32bpp_size_15x15.ico"; - public const string Bpp32_size_16x16 = "Icon/32bpp_size_16x16.ico"; - public const string Bpp32_size_17x17 = "Icon/32bpp_size_17x17.ico"; - public const string Bpp32_size_1x1 = "Icon/32bpp_size_1x1.ico"; - public const string Bpp32_size_256x256 = "Icon/32bpp_size_256x256.ico"; - public const string Bpp32_size_2x2 = "Icon/32bpp_size_2x2.ico"; - public const string Bpp32_size_31x31 = "Icon/32bpp_size_31x31.ico"; - public const string Bpp32_size_32x32 = "Icon/32bpp_size_32x32.ico"; - public const string Bpp32_size_33x33 = "Icon/32bpp_size_33x33.ico"; - public const string Bpp32_size_3x3 = "Icon/32bpp_size_3x3.ico"; - public const string Bpp32_size_4x4 = "Icon/32bpp_size_4x4.ico"; - public const string Bpp32_size_5x5 = "Icon/32bpp_size_5x5.ico"; - public const string Bpp32_size_6x6 = "Icon/32bpp_size_6x6.ico"; - public const string Bpp32_size_7x7 = "Icon/32bpp_size_7x7.ico"; - public const string Bpp32_size_8x8 = "Icon/32bpp_size_8x8.ico"; - public const string Bpp32_size_9x9 = "Icon/32bpp_size_9x9.ico"; - public const string Bpp32_transp_not_square = "Icon/32bpp_transp_not_square.ico"; - public const string Bpp32_transp_partial = "Icon/32bpp_transp_partial.ico"; - public const string Bpp32_transp = "Icon/32bpp_transp.ico"; - public const string Bpp4_size_15x15 = "Icon/4bpp_size_15x15.ico"; - public const string Bpp4_size_16x16 = "Icon/4bpp_size_16x16.ico"; - public const string Bpp4_size_17x17 = "Icon/4bpp_size_17x17.ico"; - public const string Bpp4_size_1x1 = "Icon/4bpp_size_1x1.ico"; - public const string Bpp4_size_256x256 = "Icon/4bpp_size_256x256.ico"; - public const string Bpp4_size_2x2 = "Icon/4bpp_size_2x2.ico"; - public const string Bpp4_size_31x31 = "Icon/4bpp_size_31x31.ico"; - public const string Bpp4_size_32x32 = "Icon/4bpp_size_32x32.ico"; - public const string Bpp4_size_33x33 = "Icon/4bpp_size_33x33.ico"; - public const string Bpp4_size_3x3 = "Icon/4bpp_size_3x3.ico"; - public const string Bpp4_size_4x4 = "Icon/4bpp_size_4x4.ico"; - public const string Bpp4_size_5x5 = "Icon/4bpp_size_5x5.ico"; - public const string Bpp4_size_6x6 = "Icon/4bpp_size_6x6.ico"; - public const string Bpp4_size_7x7 = "Icon/4bpp_size_7x7.ico"; - public const string Bpp4_size_8x8 = "Icon/4bpp_size_8x8.ico"; - public const string Bpp4_size_9x9 = "Icon/4bpp_size_9x9.ico"; - public const string Bpp4_transp_not_square = "Icon/4bpp_transp_not_square.ico"; - public const string Bpp4_transp_partial = "Icon/4bpp_transp_partial.ico"; - public const string Bpp8_size_15x15 = "Icon/8bpp_size_15x15.ico"; - public const string Bpp8_size_16x16 = "Icon/8bpp_size_16x16.ico"; - public const string Bpp8_size_17x17 = "Icon/8bpp_size_17x17.ico"; - public const string Bpp8_size_1x1 = "Icon/8bpp_size_1x1.ico"; - public const string Bpp8_size_256x256 = "Icon/8bpp_size_256x256.ico"; - public const string Bpp8_size_2x2 = "Icon/8bpp_size_2x2.ico"; - public const string Bpp8_size_31x31 = "Icon/8bpp_size_31x31.ico"; - public const string Bpp8_size_32x32 = "Icon/8bpp_size_32x32.ico"; - public const string Bpp8_size_33x33 = "Icon/8bpp_size_33x33.ico"; - public const string Bpp8_size_3x3 = "Icon/8bpp_size_3x3.ico"; - public const string Bpp8_size_4x4 = "Icon/8bpp_size_4x4.ico"; - public const string Bpp8_size_5x5 = "Icon/8bpp_size_5x5.ico"; - public const string Bpp8_size_6x6 = "Icon/8bpp_size_6x6.ico"; - public const string Bpp8_size_7x7 = "Icon/8bpp_size_7x7.ico"; - public const string Bpp8_size_8x8 = "Icon/8bpp_size_8x8.ico"; - public const string Bpp8_size_9x9 = "Icon/8bpp_size_9x9.ico"; - public const string Bpp8_transp_not_square = "Icon/8bpp_transp_not_square.ico"; - public const string Bpp8_transp_partial = "Icon/8bpp_transp_partial.ico"; - public const string Invalid_all = "Icon/invalid_all.ico"; + public const string Bpp1Size15x15 = "Icon/1bpp_size_15x15.ico"; + public const string Bpp1Size16x16 = "Icon/1bpp_size_16x16.ico"; + public const string Bpp1Size17x17 = "Icon/1bpp_size_17x17.ico"; + public const string Bpp1Size1x1 = "Icon/1bpp_size_1x1.ico"; + public const string Bpp1Size256x256 = "Icon/1bpp_size_256x256.ico"; + public const string Bpp1Size2x2 = "Icon/1bpp_size_2x2.ico"; + public const string Bpp1Size31x31 = "Icon/1bpp_size_31x31.ico"; + public const string Bpp1Size32x32 = "Icon/1bpp_size_32x32.ico"; + public const string Bpp1Size33x33 = "Icon/1bpp_size_33x33.ico"; + public const string Bpp1Size3x3 = "Icon/1bpp_size_3x3.ico"; + public const string Bpp1Size4x4 = "Icon/1bpp_size_4x4.ico"; + public const string Bpp1Size5x5 = "Icon/1bpp_size_5x5.ico"; + public const string Bpp1Size6x6 = "Icon/1bpp_size_6x6.ico"; + public const string Bpp1Size7x7 = "Icon/1bpp_size_7x7.ico"; + public const string Bpp1Size8x8 = "Icon/1bpp_size_8x8.ico"; + public const string Bpp1Size9x9 = "Icon/1bpp_size_9x9.ico"; + public const string Bpp1TranspNotSquare = "Icon/1bpp_transp_not_square.ico"; + public const string Bpp1TranspPartial = "Icon/1bpp_transp_partial.ico"; + public const string Bpp24Size15x15 = "Icon/24bpp_size_15x15.ico"; + public const string Bpp24Size16x16 = "Icon/24bpp_size_16x16.ico"; + public const string Bpp24Size17x17 = "Icon/24bpp_size_17x17.ico"; + public const string Bpp24Size1x1 = "Icon/24bpp_size_1x1.ico"; + public const string Bpp24Size256x256 = "Icon/24bpp_size_256x256.ico"; + public const string Bpp24Size2x2 = "Icon/24bpp_size_2x2.ico"; + public const string Bpp24Size31x31 = "Icon/24bpp_size_31x31.ico"; + public const string Bpp24Size32x32 = "Icon/24bpp_size_32x32.ico"; + public const string Bpp24Size33x33 = "Icon/24bpp_size_33x33.ico"; + public const string Bpp24Size3x3 = "Icon/24bpp_size_3x3.ico"; + public const string Bpp24Size4x4 = "Icon/24bpp_size_4x4.ico"; + public const string Bpp24Size5x5 = "Icon/24bpp_size_5x5.ico"; + public const string Bpp24Size6x6 = "Icon/24bpp_size_6x6.ico"; + public const string Bpp24Size7x7 = "Icon/24bpp_size_7x7.ico"; + public const string Bpp24Size8x8 = "Icon/24bpp_size_8x8.ico"; + public const string Bpp24Size9x9 = "Icon/24bpp_size_9x9.ico"; + public const string Bpp24TranspNotSquare = "Icon/24bpp_transp_not_square.ico"; + public const string Bpp24TranspPartial = "Icon/24bpp_transp_partial.ico"; + public const string Bpp24Transp = "Icon/24bpp_transp.ico"; + public const string Bpp32Size15x15 = "Icon/32bpp_size_15x15.ico"; + public const string Bpp32Size16x16 = "Icon/32bpp_size_16x16.ico"; + public const string Bpp32Size17x17 = "Icon/32bpp_size_17x17.ico"; + public const string Bpp32Size1x1 = "Icon/32bpp_size_1x1.ico"; + public const string Bpp32Size256x256 = "Icon/32bpp_size_256x256.ico"; + public const string Bpp32Size2x2 = "Icon/32bpp_size_2x2.ico"; + public const string Bpp32Size31x31 = "Icon/32bpp_size_31x31.ico"; + public const string Bpp32Size32x32 = "Icon/32bpp_size_32x32.ico"; + public const string Bpp32Size33x33 = "Icon/32bpp_size_33x33.ico"; + public const string Bpp32Size3x3 = "Icon/32bpp_size_3x3.ico"; + public const string Bpp32Size4x4 = "Icon/32bpp_size_4x4.ico"; + public const string Bpp32Size5x5 = "Icon/32bpp_size_5x5.ico"; + public const string Bpp32Size6x6 = "Icon/32bpp_size_6x6.ico"; + public const string Bpp32Size7x7 = "Icon/32bpp_size_7x7.ico"; + public const string Bpp32Size8x8 = "Icon/32bpp_size_8x8.ico"; + public const string Bpp32Size9x9 = "Icon/32bpp_size_9x9.ico"; + public const string Bpp32TranspNotSquare = "Icon/32bpp_transp_not_square.ico"; + public const string Bpp32TranspPartial = "Icon/32bpp_transp_partial.ico"; + public const string Bpp32Transp = "Icon/32bpp_transp.ico"; + public const string Bpp4Size15x15 = "Icon/4bpp_size_15x15.ico"; + public const string Bpp4Size16x16 = "Icon/4bpp_size_16x16.ico"; + public const string Bpp4Size17x17 = "Icon/4bpp_size_17x17.ico"; + public const string Bpp4Size1x1 = "Icon/4bpp_size_1x1.ico"; + public const string Bpp4Size256x256 = "Icon/4bpp_size_256x256.ico"; + public const string Bpp4Size2x2 = "Icon/4bpp_size_2x2.ico"; + public const string Bpp4Size31x31 = "Icon/4bpp_size_31x31.ico"; + public const string Bpp4Size32x32 = "Icon/4bpp_size_32x32.ico"; + public const string Bpp4Size33x33 = "Icon/4bpp_size_33x33.ico"; + public const string Bpp4Size3x3 = "Icon/4bpp_size_3x3.ico"; + public const string Bpp4Size4x4 = "Icon/4bpp_size_4x4.ico"; + public const string Bpp4Size5x5 = "Icon/4bpp_size_5x5.ico"; + public const string Bpp4Size6x6 = "Icon/4bpp_size_6x6.ico"; + public const string Bpp4Size7x7 = "Icon/4bpp_size_7x7.ico"; + public const string Bpp4Size8x8 = "Icon/4bpp_size_8x8.ico"; + public const string Bpp4Size9x9 = "Icon/4bpp_size_9x9.ico"; + public const string Bpp4TranspNotSquare = "Icon/4bpp_transp_not_square.ico"; + public const string Bpp4TranspPartial = "Icon/4bpp_transp_partial.ico"; + public const string Bpp8Size15x15 = "Icon/8bpp_size_15x15.ico"; + public const string Bpp8Size16x16 = "Icon/8bpp_size_16x16.ico"; + public const string Bpp8Size17x17 = "Icon/8bpp_size_17x17.ico"; + public const string Bpp8Size1x1 = "Icon/8bpp_size_1x1.ico"; + public const string Bpp8Size256x256 = "Icon/8bpp_size_256x256.ico"; + public const string Bpp8Size2x2 = "Icon/8bpp_size_2x2.ico"; + public const string Bpp8Size31x31 = "Icon/8bpp_size_31x31.ico"; + public const string Bpp8Size32x32 = "Icon/8bpp_size_32x32.ico"; + public const string Bpp8Size33x33 = "Icon/8bpp_size_33x33.ico"; + public const string Bpp8Size3x3 = "Icon/8bpp_size_3x3.ico"; + public const string Bpp8Size4x4 = "Icon/8bpp_size_4x4.ico"; + public const string Bpp8Size5x5 = "Icon/8bpp_size_5x5.ico"; + public const string Bpp8Size6x6 = "Icon/8bpp_size_6x6.ico"; + public const string Bpp8Size7x7 = "Icon/8bpp_size_7x7.ico"; + public const string Bpp8Size8x8 = "Icon/8bpp_size_8x8.ico"; + public const string Bpp8Size9x9 = "Icon/8bpp_size_9x9.ico"; + public const string Bpp8TranspNotSquare = "Icon/8bpp_transp_not_square.ico"; + public const string Bpp8TranspPartial = "Icon/8bpp_transp_partial.ico"; + public const string InvalidAll = "Icon/invalid_all.ico"; public const string IcoFake = "Icon/ico_fake.cur"; - public const string Invalid_bpp = "Icon/invalid_bpp.ico"; - public const string Invalid_compression = "Icon/invalid_compression.ico"; - public const string Invalid_png = "Icon/invalid_png.ico"; - public const string Invalid_RLE4 = "Icon/invalid_RLE4.ico"; - public const string Invalid_RLE8 = "Icon/invalid_RLE8.ico"; - public const string Mixed_bmp_png_a = "Icon/mixed_bmp_png_a.ico"; - public const string Mixed_bmp_png_b = "Icon/mixed_bmp_png_b.ico"; - public const string Mixed_bmp_png_c = "Icon/mixed_bmp_png_c.ico"; - public const string Multi_size_a = "Icon/multi_size_a.ico"; - public const string Multi_size_b = "Icon/multi_size_b.ico"; - public const string Multi_size_c = "Icon/multi_size_c.ico"; - public const string Multi_size_d = "Icon/multi_size_d.ico"; - public const string Multi_size_e = "Icon/multi_size_e.ico"; - public const string Multi_size_f = "Icon/multi_size_f.ico"; - public const string Multi_size_multi_bits_a = "Icon/multi_size_multi_bits_a.ico"; - public const string Multi_size_multi_bits_b = "Icon/multi_size_multi_bits_b.ico"; - public const string Multi_size_multi_bits_c = "Icon/multi_size_multi_bits_c.ico"; - public const string Multi_size_multi_bits_d = "Icon/multi_size_multi_bits_d.ico"; + public const string InvalidBpp = "Icon/invalid_bpp.ico"; + public const string InvalidCompression = "Icon/invalid_compression.ico"; + public const string InvalidPng = "Icon/invalid_png.ico"; + public const string InvalidRLE4 = "Icon/invalid_RLE4.ico"; + public const string InvalidRLE8 = "Icon/invalid_RLE8.ico"; + public const string MixedBmpPngA = "Icon/mixed_bmp_png_a.ico"; + public const string MixedBmpPngB = "Icon/mixed_bmp_png_b.ico"; + public const string MixedBmpPngC = "Icon/mixed_bmp_png_c.ico"; + public const string MultiSizeA = "Icon/multi_size_a.ico"; + public const string MultiSizeB = "Icon/multi_size_b.ico"; + public const string MultiSizeC = "Icon/multi_size_c.ico"; + public const string MultiSizeD = "Icon/multi_size_d.ico"; + public const string MultiSizeE = "Icon/multi_size_e.ico"; + public const string MultiSizeF = "Icon/multi_size_f.ico"; + public const string MultiSizeMultiBitsA = "Icon/multi_size_multi_bits_a.ico"; + public const string MultiSizeMultiBitsB = "Icon/multi_size_multi_bits_b.ico"; + public const string MultiSizeMultiBitsC = "Icon/multi_size_multi_bits_c.ico"; + public const string MultiSizeMultiBitsD = "Icon/multi_size_multi_bits_d.ico"; } public static class Cur From 237d129afcf96b1e9876f079cc3d99a199450658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:45:45 +0800 Subject: [PATCH 198/220] Update Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderTests.cs | 12 +++ .../Formats/Icon/Cur/CurEncoderTests.cs | 9 -- .../Formats/Icon/Ico/IcoDecoderTests.cs | 101 +++++++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 2 +- 4 files changed, 110 insertions(+), 14 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index d1587d35b..e6efda4b6 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -21,4 +21,16 @@ public class CurDecoderTests // TODO: Assert metadata, frame count, etc } + + [Theory] + [WithFile(CurFake, PixelTypes.Rgba32)] + [WithFile(CurReal, PixelTypes.Rgba32)] + public void CurDecoder_Decode2(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 58213397c..9908786f1 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Diagnostics; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -30,12 +29,4 @@ public class CurEncoderTests memStream.Seek(0, SeekOrigin.Begin); CurDecoder.Instance.Decode(new(), memStream); } - - [Theory] - [WithFile(CurFake, PixelTypes.Rgba32)] - [WithFile(CurReal, PixelTypes.Rgba32)] - public void CurDecoder_Decode2(TestImageProvider provider) - { - Debug.Assert(false); - } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 8ea7cf6d4..981a1031d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Diagnostics; using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -42,6 +41,16 @@ public class IcoDecoderTests [WithFile(Bpp1Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp1TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp1TranspPartial, PixelTypes.Rgba32)] + public void Bpp1Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp24Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp24Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp24Size17x17, PixelTypes.Rgba32)] @@ -61,6 +70,16 @@ public class IcoDecoderTests [WithFile(Bpp24TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp24TranspPartial, PixelTypes.Rgba32)] [WithFile(Bpp24Transp, PixelTypes.Rgba32)] + public void Bpp24Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp32Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp32Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp32Size17x17, PixelTypes.Rgba32)] @@ -80,6 +99,16 @@ public class IcoDecoderTests [WithFile(Bpp32TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp32TranspPartial, PixelTypes.Rgba32)] [WithFile(Bpp32Transp, PixelTypes.Rgba32)] + public void Bpp32Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp4Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp4Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp4Size17x17, PixelTypes.Rgba32)] @@ -98,6 +127,16 @@ public class IcoDecoderTests [WithFile(Bpp4Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp4TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp4TranspPartial, PixelTypes.Rgba32)] + public void Bpp4Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp8Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp8Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp8Size17x17, PixelTypes.Rgba32)] @@ -116,28 +155,82 @@ public class IcoDecoderTests [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] + public void Bpp8Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(InvalidAll, PixelTypes.Rgba32)] - [WithFile(IcoFake, PixelTypes.Rgba32)] [WithFile(InvalidBpp, PixelTypes.Rgba32)] [WithFile(InvalidCompression, PixelTypes.Rgba32)] [WithFile(InvalidPng, PixelTypes.Rgba32)] [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] + public void InvalidTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] [WithFile(MixedBmpPngC, PixelTypes.Rgba32)] + public void MixedBmpPngTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MultiSizeA, PixelTypes.Rgba32)] [WithFile(MultiSizeB, PixelTypes.Rgba32)] [WithFile(MultiSizeC, PixelTypes.Rgba32)] [WithFile(MultiSizeD, PixelTypes.Rgba32)] [WithFile(MultiSizeE, PixelTypes.Rgba32)] [WithFile(MultiSizeF, PixelTypes.Rgba32)] + public void MultiSizeTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MultiSizeMultiBitsA, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsB, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsC, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsD, PixelTypes.Rgba32)] - public void IcoDecoder_Decode2(TestImageProvider provider) + public void MultiSizeMultiBitsTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] + [WithFile(IcoFake, PixelTypes.Rgba32)] + public void IcoFakeTest(TestImageProvider provider) { - Debug.Assert(false); + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index fcb32eae6..8937799e1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1219,7 +1219,6 @@ public static class TestImages public const string Bpp8TranspNotSquare = "Icon/8bpp_transp_not_square.ico"; public const string Bpp8TranspPartial = "Icon/8bpp_transp_partial.ico"; public const string InvalidAll = "Icon/invalid_all.ico"; - public const string IcoFake = "Icon/ico_fake.cur"; public const string InvalidBpp = "Icon/invalid_bpp.ico"; public const string InvalidCompression = "Icon/invalid_compression.ico"; public const string InvalidPng = "Icon/invalid_png.ico"; @@ -1238,6 +1237,7 @@ public static class TestImages public const string MultiSizeMultiBitsB = "Icon/multi_size_multi_bits_b.ico"; public const string MultiSizeMultiBitsC = "Icon/multi_size_multi_bits_c.ico"; public const string MultiSizeMultiBitsD = "Icon/multi_size_multi_bits_d.ico"; + public const string IcoFake = "Icon/ico_fake.cur"; } public static class Cur From 1f4c7e3360fc6397be6d3f64a719f3b84b86e56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:56:34 +0800 Subject: [PATCH 199/220] Assert Invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 981a1031d..c964733c3 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -172,13 +172,14 @@ public class IcoDecoderTests [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] public void InvalidTest(TestImageProvider provider) - { - using Image image = provider.GetImage(IcoDecoder.Instance); + => Assert.Throws(() => + { + using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSaveMultiFrame(provider, extension: "png"); - // TODO: Assert metadata, frame count, etc - } + // TODO: Assert metadata, frame count, etc + }); [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] From 9a7727b3148bd461726b3cfdeab5592b57a2d273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 22:03:06 +0800 Subject: [PATCH 200/220] Pass the Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index c964733c3..1c724ca35 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -168,7 +168,6 @@ public class IcoDecoderTests [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(InvalidBpp, PixelTypes.Rgba32)] [WithFile(InvalidCompression, PixelTypes.Rgba32)] - [WithFile(InvalidPng, PixelTypes.Rgba32)] [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] public void InvalidTest(TestImageProvider provider) @@ -181,6 +180,17 @@ public class IcoDecoderTests // TODO: Assert metadata, frame count, etc }); + [Theory] + [WithFile(InvalidPng, PixelTypes.Rgba32)] + public void InvalidPngTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] From b31bd2337c57c39a62d4be09c7db68d264b0eea7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 19 Jun 2024 16:24:31 +1000 Subject: [PATCH 201/220] Complete tests and fix issues --- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 8 +- src/ImageSharp/Formats/Cur/CurEncoder.cs | 2 +- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 4 +- .../Formats/Cur/CurFrameMetadata.cs | 25 ++-- src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 8 +- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 4 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 4 +- .../Formats/Ico/IcoFrameMetadata.cs | 26 +++- .../Formats/Icon/IconDecoderCore.cs | 81 ++++++++-- .../Formats/Icon/IconEncoderCore.cs | 72 ++++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 31 +++- .../Quantization/PaletteQuantizer.cs | 7 +- .../Formats/Icon/Cur/CurDecoderTests.cs | 19 ++- .../Formats/Icon/Cur/CurEncoderTests.cs | 26 ++-- .../Formats/Icon/Ico/IcoDecoderTests.cs | 139 ++++++++++++++---- .../Formats/Icon/Ico/IcoEncoderTests.cs | 26 ++-- 16 files changed, 363 insertions(+), 119 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 18ab8c75a..3018ec6bf 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -14,11 +14,17 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) + protected override void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); curFrameMetadata.FromIconDirEntry(entry); curFrameMetadata.Compression = compression; curFrameMetadata.BmpBitsPerPixel = bitsPerPixel; + curFrameMetadata.ColorTable = colorTable; } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoder.cs b/src/ImageSharp/Formats/Cur/CurEncoder.cs index d237fe7d0..e19a73990 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoder.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoder.cs @@ -11,7 +11,7 @@ public sealed class CurEncoder : QuantizingImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - CurEncoderCore encoderCore = new(); + CurEncoderCore encoderCore = new(this); encoderCore.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index a6922d431..6435587e2 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -7,8 +7,8 @@ namespace SixLabors.ImageSharp.Formats.Cur; internal sealed class CurEncoderCore : IconEncoderCore { - public CurEncoderCore() - : base(IconFileType.CUR) + public CurEncoderCore(QuantizingImageEncoder encoder) + : base(encoder, IconFileType.CUR) { } } diff --git a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index fc5cc5b2c..014944ba6 100644 --- a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Cur; @@ -18,14 +19,14 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { } - private CurFrameMetadata(CurFrameMetadata metadata) + private CurFrameMetadata(CurFrameMetadata other) { - this.Compression = metadata.Compression; - this.HotspotX = metadata.HotspotX; - this.HotspotY = metadata.HotspotY; - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; + this.Compression = other.Compression; + this.HotspotX = other.HotspotX; + this.HotspotY = other.HotspotY; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; } /// @@ -45,13 +46,13 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable /// /// Gets or sets the encoding width.
    - /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
    public byte EncodingWidth { get; set; } /// /// Gets or sets the encoding height.
    - /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
    public byte EncodingHeight { get; set; } @@ -61,6 +62,12 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable ///
    public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + /// + /// Gets or sets the color table, if any. + /// The underlying pixel format is represented by . + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public CurFrameMetadata DeepClone() => new(this); diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index e8629e35b..f4990c66a 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -14,11 +14,17 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) + protected override void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); icoFrameMetadata.FromIconDirEntry(entry); icoFrameMetadata.Compression = compression; icoFrameMetadata.BmpBitsPerPixel = bitsPerPixel; + icoFrameMetadata.ColorTable = colorTable; } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs index 298f93dec..e72925156 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Formats.Ico; /// /// Image encoder for writing an image to a stream as a Windows Icon. /// -public sealed class IcoEncoder : ImageEncoder +public sealed class IcoEncoder : QuantizingImageEncoder { /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - IcoEncoderCore encoderCore = new(); + IcoEncoderCore encoderCore = new(this); encoderCore.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index ab3edfbd3..f3cacb1b9 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -7,8 +7,8 @@ namespace SixLabors.ImageSharp.Formats.Ico; internal sealed class IcoEncoderCore : IconEncoderCore { - public IcoEncoderCore() - : base(IconFileType.ICO) + public IcoEncoderCore(QuantizingImageEncoder encoder) + : base(encoder, IconFileType.ICO) { } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 82e4ce3b2..ea27d13c8 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Ico; @@ -18,12 +19,17 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { } - private IcoFrameMetadata(IcoFrameMetadata metadata) + private IcoFrameMetadata(IcoFrameMetadata other) { - this.Compression = metadata.Compression; - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; + this.Compression = other.Compression; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } } /// @@ -33,13 +39,13 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable /// /// Gets or sets the encoding width.
    - /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
    public byte EncodingWidth { get; set; } /// /// Gets or sets the encoding height.
    - /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
    public byte EncodingHeight { get; set; } @@ -49,6 +55,12 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable ///
    public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + /// + /// Gets or sets the color table, if any. + /// The underlying pixel format is represented by . + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public IcoFrameMetadata DeepClone() => new(this); diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index a0849fa61..74fe7b9e5 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -31,10 +31,16 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries + = new((int)Math.Min(this.entries.Length, this.Options.MaxFrames)); for (int i = 0; i < this.entries.Length; i++) { + if (i == this.Options.MaxFrames) + { + break; + } + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -69,6 +75,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + ReadOnlyMemory? colorTable = null; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -88,11 +95,22 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } else { - bmpMetadata = x.Image.Metadata.GetBmpMetadata(); - bitsPerPixel = bmpMetadata.BitsPerPixel; + BmpMetadata meta = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = meta.BitsPerPixel; + colorTable = meta.ColorTable; + + if (x.Index == 0) + { + bmpMetadata = meta; + } } - this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); + this.SetFrameMetadata( + target.Metadata, + this.entries[x.Index], + x.Compression, + bitsPerPixel, + colorTable); x.Image.Dispose(); @@ -122,11 +140,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; ImageMetadata metadata = new(); - ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; + BmpMetadata? bmpMetadata = null; + PngMetadata? pngMetadata = null; + ImageFrameMetadata[] frames = new ImageFrameMetadata[Math.Min(this.fileHeader.Count, this.Options.MaxFrames)]; int bpp = 0; for (int i = 0; i < frames.Length; i++) { BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + ReadOnlyMemory? colorTable = null; ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -149,25 +170,65 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); - frames[i] = new(); - if (!isPng) + ImageFrameMetadata frameMetadata = new(); + + if (isPng) + { + if (i == 0) + { + pngMetadata = temp.Metadata.GetPngMetadata(); + } + + frameMetadata.SetFormatMetadata(PngFormat.Instance, temp.FrameMetadataCollection[0].GetPngMetadata()); + } + else { - bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; + BmpMetadata meta = temp.Metadata.GetBmpMetadata(); + bitsPerPixel = meta.BitsPerPixel; + colorTable = meta.ColorTable; + + if (i == 0) + { + bmpMetadata = meta; + } } bpp = Math.Max(bpp, (int)bitsPerPixel); - this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + frames[i] = frameMetadata; + + this.SetFrameMetadata( + frames[i], + this.entries[i], + isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, + bitsPerPixel, + colorTable); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } + // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + + if (pngMetadata != null) + { + metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); + } + return new(new(bpp), this.Dimensions, metadata, frames); } - protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); + protected abstract void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable); [MemberNotNull(nameof(entries))] protected void ReadHeader(Stream stream) diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index eb07ab483..243339661 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -13,12 +13,16 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal abstract class IconEncoderCore : IImageEncoderInternals { + private readonly QuantizingImageEncoder encoder; private readonly IconFileType iconFileType; private IconDir fileHeader; private EncodingFrameMetadata[]? entries; - protected IconEncoderCore(IconFileType iconFileType) - => this.iconFileType = iconFileType; + protected IconEncoderCore(QuantizingImageEncoder encoder, IconFileType iconFileType) + { + this.encoder = encoder; + this.iconFileType = iconFileType; + } public void Encode( Image image, @@ -71,14 +75,7 @@ internal abstract class IconEncoderCore : IImageEncoderInternals { IconFrameCompression.Bmp => new BmpEncoder() { - // We don't have access to the palette in the metadata so we need to quantize the image - // using a new one generated from the pixel data. - Quantizer = encodingMetadata.Entry.BitCount <= 8 - ? new WuQuantizer(new() - { - MaxColors = encodingMetadata.Entry.ColorCount - }) - : null, + Quantizer = this.GetQuantizer(encodingMetadata), ProcessedAlphaMask = true, UseDoubleHeight = true, SkipFileHeader = true, @@ -122,28 +119,65 @@ internal abstract class IconEncoderCore : IImageEncoderInternals image.Frames.Select(i => { IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry()); }).ToArray(), IconFileType.CUR => image.Frames.Select(i => { CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry()); }).ToArray(), _ => throw new NotSupportedException(), }; } - internal sealed class EncodingFrameMetadata( - IconFrameCompression compression, - BmpBitsPerPixel bmpBitsPerPixel, - IconDirEntry iconDirEntry) + private IQuantizer? GetQuantizer(EncodingFrameMetadata metadata) { - private IconDirEntry iconDirEntry = iconDirEntry; + if (metadata.Entry.BitCount > 8) + { + return null; + } + + if (this.encoder.Quantizer is not null) + { + return this.encoder.Quantizer; + } + + if (metadata.ColorTable is null) + { + return new WuQuantizer(new() + { + MaxColors = metadata.Entry.ColorCount + }); + } + + // Don't dither if we have a palette. We want to preserve as much information as possible. + return new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null }); + } + + internal sealed class EncodingFrameMetadata + { + private IconDirEntry iconDirEntry; + + public EncodingFrameMetadata( + IconFrameCompression compression, + BmpBitsPerPixel bmpBitsPerPixel, + ReadOnlyMemory? colorTable, + IconDirEntry iconDirEntry) + { + this.Compression = compression; + this.BmpBitsPerPixel = compression == IconFrameCompression.Png + ? BmpBitsPerPixel.Pixel32 + : bmpBitsPerPixel; + this.ColorTable = colorTable; + this.iconDirEntry = iconDirEntry; + } + + public IconFrameCompression Compression { get; } - public IconFrameCompression Compression { get; set; } = compression; + public BmpBitsPerPixel BmpBitsPerPixel { get; } - public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = bmpBitsPerPixel; + public ReadOnlyMemory? ColorTable { get; set; } public ref IconDirEntry Entry => ref this.iconDirEntry; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 36a0a8bcb..3e278be14 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -345,9 +345,10 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { uint frameCount = 0; ImageMetadata metadata = new(); + List framesMetadata = []; PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; - FrameControl? lastFrameControl = null; + FrameControl? currentFrameControl = null; Span buffer = stackalloc byte[20]; this.currentStream.Skip(8); @@ -400,7 +401,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals break; } - lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); + currentFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); + break; case PngChunkType.FrameData: if (frameCount == this.maxFrames) @@ -413,22 +415,35 @@ internal sealed class PngDecoderCore : IImageDecoderInternals goto EOF; } - if (lastFrameControl is null) + if (currentFrameControl is null) { PngThrowHelper.ThrowMissingFrameControl(); } + InitializeFrameMetadata(framesMetadata, currentFrameControl.Value); + // Skip sequence number this.currentStream.Skip(4); this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Data: + // Spec says tRNS must be before IDAT so safe to exit. if (this.colorMetadataOnly) { goto EOF; } + pngMetadata.AnimateRootFrame = currentFrameControl != null; + currentFrameControl ??= new((uint)this.header.Width, (uint)this.header.Height); + if (framesMetadata.Count == 0) + { + InitializeFrameMetadata(framesMetadata, currentFrameControl.Value); + + // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. + AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); + } + this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Palette: @@ -515,7 +530,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); - return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata); + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata, framesMetadata); } finally { @@ -680,6 +695,14 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.scanline = this.configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); } + private static void InitializeFrameMetadata(List imageFrameMetadata, FrameControl currentFrameControl) + { + ImageFrameMetadata meta = new(); + PngFrameMetadata frameMetadata = meta.GetPngMetadata(); + frameMetadata.FromChunk(currentFrameControl); + imageFrameMetadata.Add(meta); + } + /// /// Calculates the correct number of bits per pixel for the given color type. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index acd179ffc..13a59a26d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -62,9 +62,10 @@ public class PaletteQuantizer : IQuantizer { Guard.NotNull(options, nameof(options)); - // Always use the palette length over options since the palette cannot be reduced. - TPixel[] palette = new TPixel[this.colorPalette.Length]; - Color.ToPixel(this.colorPalette.Span, palette.AsSpan()); + // If the palette is larger than the max colors then we need to trim it down. + // treat the buffer as FILO. + TPixel[] palette = new TPixel[Math.Min(options.MaxColors, this.colorPalette.Length)]; + Color.ToPixel(this.colorPalette.Span[..palette.Length], palette.AsSpan()); return new PaletteQuantizer(configuration, options, palette, this.transparentIndex); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index e6efda4b6..4efd33648 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -17,9 +19,11 @@ public class CurDecoderTests { using Image image = provider.GetImage(CurDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth); + Assert.Equal(image.Height, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -28,9 +32,10 @@ public class CurDecoderTests public void CurDecoder_Decode2(TestImageProvider provider) { using Image image = provider.GetImage(CurDecoder.Instance); - - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth); + Assert.Equal(image.Height, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 9908786f1..b9b66296d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Cur; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; @@ -10,23 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; [Trait("Format", "Cur")] public class CurEncoderTests { - private static CurEncoder CurEncoder => new(); - - public static readonly TheoryData Files = new() - { - { WindowsMouse }, - }; + private static CurEncoder Encoder => new(); [Theory] - [MemberData(nameof(Files))] - public void Encode(string imagePath) + [WithFile(CurReal, PixelTypes.Rgba32)] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - TestFile testFile = TestFile.Create(imagePath); - using Image input = testFile.CreateRgba32Image(); + using Image image = provider.GetImage(CurDecoder.Instance); using MemoryStream memStream = new(); - input.Save(memStream, CurEncoder); + image.DebugSaveMultiFrame(provider); + image.Save(memStream, Encoder); memStream.Seek(0, SeekOrigin.Begin); - CurDecoder.Instance.Decode(new(), memStream); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, CurDecoder.Instance); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 1c724ca35..a776a637b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -17,9 +19,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSaveMultiFrame(provider); - // TODO: Assert metadata, frame count, etc + Assert.Equal(10, image.Frames.Count); } [Theory] @@ -45,9 +47,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); - // TODO: Assert metadata, frame count, etc + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; + + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel1, meta.BmpBitsPerPixel); } [Theory] @@ -74,9 +83,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel24, meta.BmpBitsPerPixel); } [Theory] @@ -103,9 +119,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); - // TODO: Assert metadata, frame count, etc + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; + + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -131,9 +154,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel4, meta.BmpBitsPerPixel); } [Theory] @@ -151,7 +181,8 @@ public class IcoDecoderTests [WithFile(Bpp8Size5x5, PixelTypes.Rgba32)] [WithFile(Bpp8Size6x6, PixelTypes.Rgba32)] [WithFile(Bpp8Size7x7, PixelTypes.Rgba32)] - [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] + + // [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] This is actually 24 bit. [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] @@ -159,9 +190,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel8, meta.BmpBitsPerPixel); } [Theory] @@ -174,10 +212,6 @@ public class IcoDecoderTests => Assert.Throws(() => { using Image image = provider.GetImage(IcoDecoder.Instance); - - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc }); [Theory] @@ -186,9 +220,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Png, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -199,9 +240,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); - // TODO: Assert metadata, frame count, etc + image.DebugSaveMultiFrame(provider); } [Theory] @@ -215,9 +256,46 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); + + for (int i = 0; i < image.Frames.Count; i++) + { + ImageFrame frame = image.Frames[i]; + IcoFrameMetadata meta = frame.Metadata.GetIcoMetadata(); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + } + + image.DebugSaveMultiFrame(provider); + } + + [Theory] + [WithFile(MultiSizeA, PixelTypes.Rgba32)] + [WithFile(MultiSizeB, PixelTypes.Rgba32)] + [WithFile(MultiSizeC, PixelTypes.Rgba32)] + [WithFile(MultiSizeD, PixelTypes.Rgba32)] + [WithFile(MultiSizeE, PixelTypes.Rgba32)] + [WithFile(MultiSizeF, PixelTypes.Rgba32)] + public void MultiSize_CanDecodeSingleFrame(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance, new() { MaxFrames = 1 }); + Assert.Single(image.Frames); + } - // TODO: Assert metadata, frame count, etc + [Theory] + [InlineData(MultiSizeA)] + [InlineData(MultiSizeB)] + [InlineData(MultiSizeC)] + [InlineData(MultiSizeD)] + [InlineData(MultiSizeE)] + [InlineData(MultiSizeF)] + public void MultiSize_CanIdentifySingleFrame(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(new() { MaxFrames = 1 }, stream); + + Assert.Single(imageInfo.FrameMetadataCollection); } [Theory] @@ -229,9 +307,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); - // TODO: Assert metadata, frame count, etc + image.DebugSaveMultiFrame(provider); } [Theory] @@ -240,8 +318,15 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs index 9a239bdd4..db28f9f70 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Ico; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; @@ -10,23 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; [Trait("Format", "Icon")] public class IcoEncoderTests { - private static IcoEncoder CurEncoder => new(); - - public static readonly TheoryData Files = new() - { - { Flutter }, - }; + private static IcoEncoder Encoder => new(); [Theory] - [MemberData(nameof(Files))] - public void Encode(string imagePath) + [WithFile(Flutter, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - TestFile testFile = TestFile.Create(imagePath); - using Image input = testFile.CreateRgba32Image(); + using Image image = provider.GetImage(IcoDecoder.Instance); using MemoryStream memStream = new(); - input.Save(memStream, CurEncoder); + image.DebugSaveMultiFrame(provider); + image.Save(memStream, Encoder); memStream.Seek(0, SeekOrigin.Begin); - IcoDecoder.Instance.Decode(new(), memStream); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + + // Despite preservation of the palette. The process can still be lossy + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.TolerantPercentage(.23f), IcoDecoder.Instance); } } From 9bdbc526da7a13e62f22677d71f316867a95f078 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 19 Jun 2024 22:59:39 +1000 Subject: [PATCH 202/220] Migrate ICO and CUR --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 3 + src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 18 +- .../Formats/Cur/CurFrameMetadata.cs | 133 ++++++++++- src/ImageSharp/Formats/Cur/CurMetadata.cs | 173 ++++++++++++++- .../Formats/Cur/MetadataExtensions.cs | 45 ---- .../Formats/FormatConnectingFrameMetadata.cs | 39 +++- .../Formats/FormatConnectingMetadata.cs | 2 +- src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 16 +- .../Formats/Ico/IcoFrameMetadata.cs | 133 ++++++++++- src/ImageSharp/Formats/Ico/IcoMetadata.cs | 159 +++++++++++++- .../Formats/Ico/MetadataExtensions.cs | 45 ---- .../Formats/Icon/IconDecoderCore.cs | 14 +- .../Formats/Icon/IconEncoderCore.cs | 2 +- .../_Generated/ImageExtensions.Save.cs | 206 ++++++++++++++++++ .../_Generated/ImageMetadataExtensions.cs | 82 +++++++ .../Formats/_Generated/_Formats.ttinclude | 4 + src/ImageSharp/Metadata/ImageFrameMetadata.cs | 6 +- src/ImageSharp/Metadata/ImageMetadata.cs | 6 +- .../Formats/Icon/Cur/CurDecoderTests.cs | 4 +- .../Formats/Icon/Cur/CurEncoderTests.cs | 34 ++- .../Formats/Icon/Ico/IcoDecoderTests.cs | 16 +- .../Formats/Icon/Ico/IcoEncoderTests.cs | 33 ++- 22 files changed, 1034 insertions(+), 139 deletions(-) delete mode 100644 src/ImageSharp/Formats/Cur/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Ico/MetadataExtensions.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index 707dd34c4..68e99bdc5 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -143,6 +143,9 @@ public class BmpMetadata : IFormatMetadata public FormatConnectingMetadata ToFormatConnectingMetadata() => new() { + EncodingType = this.BitsPerPixel <= BmpBitsPerPixel.Bit8 + ? EncodingType.Lossy + : EncodingType.Lossless, PixelTypeInfo = this.GetPixelTypeInfo() }; diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 3018ec6bf..a8a51878e 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -15,16 +15,30 @@ internal sealed class CurDecoderCore : IconDecoderCore } protected override void SetFrameMetadata( - ImageFrameMetadata metadata, + ImageMetadata imageMetadata, + ImageFrameMetadata frameMetadata, + int index, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel, ReadOnlyMemory? colorTable) { - CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); + CurFrameMetadata curFrameMetadata = frameMetadata.GetCurMetadata(); curFrameMetadata.FromIconDirEntry(entry); curFrameMetadata.Compression = compression; curFrameMetadata.BmpBitsPerPixel = bitsPerPixel; curFrameMetadata.ColorTable = colorTable; + + if (index == 0) + { + CurMetadata curMetadata = imageMetadata.GetCurMetadata(); + curMetadata.Compression = compression; + curMetadata.BmpBitsPerPixel = bitsPerPixel; + curMetadata.ColorTable = colorTable; + curMetadata.EncodingWidth = curFrameMetadata.EncodingWidth; + curMetadata.EncodingHeight = curFrameMetadata.EncodingHeight; + curMetadata.HotspotX = curFrameMetadata.HotspotX; + curMetadata.HotspotY = curFrameMetadata.HotspotY; + } } } diff --git a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index 014944ba6..06cf426dc 100644 --- a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Cur; /// /// IcoFrameMetadata. /// -public class CurFrameMetadata : IDeepCloneable, IDeepCloneable +public class CurFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -60,7 +60,7 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable /// Gets or sets the number of bits per pixel.
    /// Used when is ///
    - public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32; /// /// Gets or sets the color table, if any. @@ -69,11 +69,75 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable public ReadOnlyMemory? ColorTable { get; set; } /// - public CurFrameMetadata DeepClone() => new(this); + public static CurFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + { + if (!metadata.PixelTypeInfo.HasValue) + { + return new CurFrameMetadata + { + BmpBitsPerPixel = BmpBitsPerPixel.Bit32, + Compression = IconFrameCompression.Png + }; + } + + byte encodingWidth = metadata.EncodingWidth switch + { + > 255 => 0, + <= 255 and >= 1 => (byte)metadata.EncodingWidth, + _ => 0 + }; + + byte encodingHeight = metadata.EncodingHeight switch + { + > 255 => 0, + <= 255 and >= 1 => (byte)metadata.EncodingHeight, + _ => 0 + }; + + int bpp = metadata.PixelTypeInfo.Value.BitsPerPixel; + BmpBitsPerPixel bbpp = bpp switch + { + 1 => BmpBitsPerPixel.Bit1, + 2 => BmpBitsPerPixel.Bit2, + <= 4 => BmpBitsPerPixel.Bit4, + <= 8 => BmpBitsPerPixel.Bit8, + <= 16 => BmpBitsPerPixel.Bit16, + <= 24 => BmpBitsPerPixel.Bit24, + _ => BmpBitsPerPixel.Bit32 + }; + + IconFrameCompression compression = IconFrameCompression.Bmp; + if (bbpp is BmpBitsPerPixel.Bit32) + { + compression = IconFrameCompression.Png; + } + + return new CurFrameMetadata + { + BmpBitsPerPixel = bbpp, + Compression = compression, + EncodingWidth = encodingWidth, + EncodingHeight = encodingHeight, + ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null + }; + } + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + ColorTable = this.ColorTable, + EncodingWidth = this.EncodingWidth, + EncodingHeight = this.EncodingHeight + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + /// + public CurFrameMetadata DeepClone() => new(this); + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; @@ -84,7 +148,7 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable internal IconDirEntry ToIconDirEntry() { - byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Bit8 ? (byte)0 : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); @@ -97,4 +161,65 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable ColorCount = colorCount }; } + + private PixelTypeInfo GetPixelTypeInfo() + { + int bpp = (int)this.BmpBitsPerPixel; + PixelComponentInfo info; + PixelColorType color; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + + if (this.Compression is IconFrameCompression.Png) + { + bpp = 32; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + } + else + { + switch (this.BmpBitsPerPixel) + { + case BmpBitsPerPixel.Bit1: + info = PixelComponentInfo.Create(1, bpp, 1); + color = PixelColorType.Binary; + break; + case BmpBitsPerPixel.Bit2: + info = PixelComponentInfo.Create(1, bpp, 2); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit4: + info = PixelComponentInfo.Create(1, bpp, 4); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Indexed; + break; + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + case BmpBitsPerPixel.Bit16: + info = PixelComponentInfo.Create(3, bpp, 5, 6, 5); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + } + + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + }; + } } diff --git a/src/ImageSharp/Formats/Cur/CurMetadata.cs b/src/ImageSharp/Formats/Cur/CurMetadata.cs index 5c3486d4a..6e97a8584 100644 --- a/src/ImageSharp/Formats/Cur/CurMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurMetadata.cs @@ -1,16 +1,183 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Cur; /// -/// Provides Ico specific metadata information for the image. +/// Provides Cur specific metadata information for the image. /// -public class CurMetadata : IDeepCloneable, IDeepCloneable +public class CurMetadata : IFormatMetadata { + /// + /// Initializes a new instance of the class. + /// + public CurMetadata() + { + } + + private CurMetadata(CurMetadata other) + { + this.Compression = other.Compression; + this.HotspotX = other.HotspotX; + this.HotspotY = other.HotspotY; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } + } + + /// + /// Gets or sets the frame compressions format. Derived from the root frame. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets the horizontal coordinates of the hotspot in number of pixels from the left. Derived from the root frame. + /// + public ushort HotspotX { get; set; } + + /// + /// Gets or sets the vertical coordinates of the hotspot in number of pixels from the top. Derived from the root frame. + /// + public ushort HotspotY { get; set; } + + /// + /// Gets or sets the encoding width.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame. + ///
    + public byte EncodingWidth { get; set; } + + /// + /// Gets or sets the encoding height.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame. + ///
    + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets the number of bits per pixel.
    + /// Used when is + ///
    + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32; + + /// + /// Gets or sets the color table, if any. Derived from the root frame.
    + /// The underlying pixel format is represented by . + ///
    + public ReadOnlyMemory? ColorTable { get; set; } + /// - public CurMetadata DeepClone() => new(); + public static CurMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + BmpBitsPerPixel bbpp = bpp switch + { + 1 => BmpBitsPerPixel.Bit1, + 2 => BmpBitsPerPixel.Bit2, + <= 4 => BmpBitsPerPixel.Bit4, + <= 8 => BmpBitsPerPixel.Bit8, + <= 16 => BmpBitsPerPixel.Bit16, + <= 24 => BmpBitsPerPixel.Bit24, + _ => BmpBitsPerPixel.Bit32 + }; + + IconFrameCompression compression = IconFrameCompression.Bmp; + if (bbpp is BmpBitsPerPixel.Bit32) + { + compression = IconFrameCompression.Png; + } + + return new CurMetadata + { + BmpBitsPerPixel = bbpp, + Compression = compression, + ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null + }; + } + + /// + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp = (int)this.BmpBitsPerPixel; + PixelComponentInfo info; + PixelColorType color; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + + if (this.Compression is IconFrameCompression.Png) + { + bpp = 32; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + } + else + { + switch (this.BmpBitsPerPixel) + { + case BmpBitsPerPixel.Bit1: + info = PixelComponentInfo.Create(1, bpp, 1); + color = PixelColorType.Binary; + break; + case BmpBitsPerPixel.Bit2: + info = PixelComponentInfo.Create(1, bpp, 2); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit4: + info = PixelComponentInfo.Create(1, bpp, 4); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Indexed; + break; + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + case BmpBitsPerPixel.Bit16: + info = PixelComponentInfo.Create(3, bpp, 5, 6, 5); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + } + + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + EncodingType = this.Compression == IconFrameCompression.Bmp && this.BmpBitsPerPixel <= BmpBitsPerPixel.Bit8 + ? EncodingType.Lossy + : EncodingType.Lossless, + PixelTypeInfo = this.GetPixelTypeInfo(), + ColorTable = this.ColorTable + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public CurMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs deleted file mode 100644 index 6394c564b..000000000 --- a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats.Cur; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the Icon format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static CurMetadata GetCurMetadata(this ImageMetadata source) - => source.GetFormatMetadata(CurFormat.Instance); - - /// - /// Gets the Icon format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source) - => source.GetFormatMetadata(CurFormat.Instance); - - /// - /// Gets the Icon format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified frame, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the Icon frame metadata exists; otherwise, . - /// - public static bool TryGetCurMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out CurFrameMetadata? metadata) - => source.TryGetFormatMetadata(CurFormat.Instance, out metadata); -} diff --git a/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs b/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs index 31555afe3..ded220c9a 100644 --- a/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingFrameMetadata.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats; /// @@ -9,27 +11,44 @@ namespace SixLabors.ImageSharp.Formats; public class FormatConnectingFrameMetadata { /// - /// Gets or sets the frame color table. + /// Gets information about the encoded pixel type if any. + /// + public PixelTypeInfo? PixelTypeInfo { get; init; } + + /// + /// Gets the frame color table if any. + /// + public ReadOnlyMemory? ColorTable { get; init; } + + /// + /// Gets the frame color table mode. + /// + public FrameColorTableMode ColorTableMode { get; init; } + + /// + /// Gets the duration of the frame. /// - public ReadOnlyMemory? ColorTable { get; set; } + public TimeSpan Duration { get; init; } /// - /// Gets or sets the frame color table mode. + /// Gets the frame alpha blending mode. /// - public FrameColorTableMode ColorTableMode { get; set; } + public FrameBlendMode BlendMode { get; init; } /// - /// Gets or sets the duration of the frame. + /// Gets the frame disposal mode. /// - public TimeSpan Duration { get; set; } + public FrameDisposalMode DisposalMode { get; init; } /// - /// Gets or sets the frame alpha blending mode. + /// Gets or sets the encoding width.
    + /// Used for formats that require a specific frame size. ///
    - public FrameBlendMode BlendMode { get; set; } + public int? EncodingWidth { get; set; } /// - /// Gets or sets the frame disposal mode. + /// Gets or sets the encoding height.
    + /// Used for formats that require a specific frame size. ///
    - public FrameDisposalMode DisposalMode { get; set; } + public int? EncodingHeight { get; set; } } diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs index 07579c09e..baf0a3545 100644 --- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -29,7 +29,7 @@ public class FormatConnectingMetadata public PixelTypeInfo PixelTypeInfo { get; init; } /// - /// Gets the shared color table. + /// Gets the shared color table if any. /// public ReadOnlyMemory? ColorTable { get; init; } diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index f4990c66a..8b59974eb 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -15,16 +15,28 @@ internal sealed class IcoDecoderCore : IconDecoderCore } protected override void SetFrameMetadata( - ImageFrameMetadata metadata, + ImageMetadata imageMetadata, + ImageFrameMetadata frameMetadata, + int index, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel, ReadOnlyMemory? colorTable) { - IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); + IcoFrameMetadata icoFrameMetadata = frameMetadata.GetIcoMetadata(); icoFrameMetadata.FromIconDirEntry(entry); icoFrameMetadata.Compression = compression; icoFrameMetadata.BmpBitsPerPixel = bitsPerPixel; icoFrameMetadata.ColorTable = colorTable; + + if (index == 0) + { + IcoMetadata curMetadata = imageMetadata.GetIcoMetadata(); + curMetadata.Compression = compression; + curMetadata.BmpBitsPerPixel = bitsPerPixel; + curMetadata.ColorTable = colorTable; + curMetadata.EncodingWidth = icoFrameMetadata.EncodingWidth; + curMetadata.EncodingHeight = icoFrameMetadata.EncodingHeight; + } } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index ea27d13c8..c244e3898 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Ico; /// /// Provides Ico specific metadata information for the image frame. /// -public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable +public class IcoFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -53,7 +53,7 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable /// Gets or sets the number of bits per pixel.
    /// Used when is ///
    - public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32; /// /// Gets or sets the color table, if any. @@ -62,11 +62,75 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable public ReadOnlyMemory? ColorTable { get; set; } /// - public IcoFrameMetadata DeepClone() => new(this); + public static IcoFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) + { + if (!metadata.PixelTypeInfo.HasValue) + { + return new IcoFrameMetadata + { + BmpBitsPerPixel = BmpBitsPerPixel.Bit32, + Compression = IconFrameCompression.Png + }; + } + + byte encodingWidth = metadata.EncodingWidth switch + { + > 255 => 0, + <= 255 and >= 1 => (byte)metadata.EncodingWidth, + _ => 0 + }; + + byte encodingHeight = metadata.EncodingHeight switch + { + > 255 => 0, + <= 255 and >= 1 => (byte)metadata.EncodingHeight, + _ => 0 + }; + + int bpp = metadata.PixelTypeInfo.Value.BitsPerPixel; + BmpBitsPerPixel bbpp = bpp switch + { + 1 => BmpBitsPerPixel.Bit1, + 2 => BmpBitsPerPixel.Bit2, + <= 4 => BmpBitsPerPixel.Bit4, + <= 8 => BmpBitsPerPixel.Bit8, + <= 16 => BmpBitsPerPixel.Bit16, + <= 24 => BmpBitsPerPixel.Bit24, + _ => BmpBitsPerPixel.Bit32 + }; + + IconFrameCompression compression = IconFrameCompression.Bmp; + if (bbpp is BmpBitsPerPixel.Bit32) + { + compression = IconFrameCompression.Png; + } + + return new IcoFrameMetadata + { + BmpBitsPerPixel = bbpp, + Compression = compression, + EncodingWidth = encodingWidth, + EncodingHeight = encodingHeight, + ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null + }; + } + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + ColorTable = this.ColorTable, + EncodingWidth = this.EncodingWidth, + EncodingHeight = this.EncodingHeight + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + /// + public IcoFrameMetadata DeepClone() => new(this); + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; @@ -75,7 +139,7 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable internal IconDirEntry ToIconDirEntry() { - byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Bit8 ? (byte)0 : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); @@ -92,4 +156,65 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable }, }; } + + private PixelTypeInfo GetPixelTypeInfo() + { + int bpp = (int)this.BmpBitsPerPixel; + PixelComponentInfo info; + PixelColorType color; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + + if (this.Compression is IconFrameCompression.Png) + { + bpp = 32; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + } + else + { + switch (this.BmpBitsPerPixel) + { + case BmpBitsPerPixel.Bit1: + info = PixelComponentInfo.Create(1, bpp, 1); + color = PixelColorType.Binary; + break; + case BmpBitsPerPixel.Bit2: + info = PixelComponentInfo.Create(1, bpp, 2); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit4: + info = PixelComponentInfo.Create(1, bpp, 4); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Indexed; + break; + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + case BmpBitsPerPixel.Bit16: + info = PixelComponentInfo.Create(3, bpp, 5, 6, 5); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + } + + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + }; + } } diff --git a/src/ImageSharp/Formats/Ico/IcoMetadata.cs b/src/ImageSharp/Formats/Ico/IcoMetadata.cs index f165bf916..7e31468ec 100644 --- a/src/ImageSharp/Formats/Ico/IcoMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoMetadata.cs @@ -1,16 +1,171 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Ico; /// /// Provides Ico specific metadata information for the image. /// -public class IcoMetadata : IDeepCloneable, IDeepCloneable +public class IcoMetadata : IFormatMetadata { + /// + /// Initializes a new instance of the class. + /// + public IcoMetadata() + { + } + + private IcoMetadata(IcoMetadata other) + { + this.Compression = other.Compression; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } + } + + /// + /// Gets or sets the frame compressions format. Derived from the root frame. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets the encoding width.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame. + ///
    + public byte EncodingWidth { get; set; } + + /// + /// Gets or sets the encoding height.
    + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame. + ///
    + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets the number of bits per pixel.
    + /// Used when is + ///
    + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32; + + /// + /// Gets or sets the color table, if any. Derived from the root frame.
    + /// The underlying pixel format is represented by . + ///
    + public ReadOnlyMemory? ColorTable { get; set; } + + /// + public static IcoMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) + { + int bpp = metadata.PixelTypeInfo.BitsPerPixel; + BmpBitsPerPixel bbpp = bpp switch + { + 1 => BmpBitsPerPixel.Bit1, + 2 => BmpBitsPerPixel.Bit2, + <= 4 => BmpBitsPerPixel.Bit4, + <= 8 => BmpBitsPerPixel.Bit8, + <= 16 => BmpBitsPerPixel.Bit16, + <= 24 => BmpBitsPerPixel.Bit24, + _ => BmpBitsPerPixel.Bit32 + }; + + IconFrameCompression compression = IconFrameCompression.Bmp; + if (bbpp is BmpBitsPerPixel.Bit32) + { + compression = IconFrameCompression.Png; + } + + return new IcoMetadata + { + BmpBitsPerPixel = bbpp, + Compression = compression, + ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null + }; + } + + /// + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp = (int)this.BmpBitsPerPixel; + PixelComponentInfo info; + PixelColorType color; + PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None; + + if (this.Compression is IconFrameCompression.Png) + { + bpp = 32; + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + } + else + { + switch (this.BmpBitsPerPixel) + { + case BmpBitsPerPixel.Bit1: + info = PixelComponentInfo.Create(1, bpp, 1); + color = PixelColorType.Binary; + break; + case BmpBitsPerPixel.Bit2: + info = PixelComponentInfo.Create(1, bpp, 2); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit4: + info = PixelComponentInfo.Create(1, bpp, 4); + color = PixelColorType.Indexed; + break; + case BmpBitsPerPixel.Bit8: + info = PixelComponentInfo.Create(1, bpp, 8); + color = PixelColorType.Indexed; + break; + + // Could be 555 with padding but 565 is more common in newer bitmaps and offers + // greater accuracy due to extra green precision. + case BmpBitsPerPixel.Bit16: + info = PixelComponentInfo.Create(3, bpp, 5, 6, 5); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit24: + info = PixelComponentInfo.Create(3, bpp, 8, 8, 8); + color = PixelColorType.RGB; + break; + case BmpBitsPerPixel.Bit32 or _: + info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8); + color = PixelColorType.RGB | PixelColorType.Alpha; + alpha = PixelAlphaRepresentation.Unassociated; + break; + } + } + + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color + }; + } + /// - public IcoMetadata DeepClone() => new(); + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + EncodingType = this.Compression == IconFrameCompression.Bmp && this.BmpBitsPerPixel <= BmpBitsPerPixel.Bit8 + ? EncodingType.Lossy + : EncodingType.Lossless, + PixelTypeInfo = this.GetPixelTypeInfo(), + ColorTable = this.ColorTable + }; /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public IcoMetadata DeepClone() => new(this); } diff --git a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs deleted file mode 100644 index 497375f99..000000000 --- a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats.Ico; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the Ico format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static IcoMetadata GetIcoMetadata(this ImageMetadata source) - => source.GetFormatMetadata(IcoFormat.Instance); - - /// - /// Gets the Ico format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static IcoFrameMetadata GetIcoMetadata(this ImageFrameMetadata source) - => source.GetFormatMetadata(IcoFormat.Instance); - - /// - /// Gets the Ico format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified frame, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the Ico frame metadata exists; otherwise, . - /// - public static bool TryGetIcoMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out IcoFrameMetadata? metadata) - => source.TryGetFormatMetadata(IcoFormat.Instance, out metadata); -} diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 74fe7b9e5..0feed7f69 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -74,7 +74,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { - BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Bit32; ReadOnlyMemory? colorTable = null; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; @@ -106,7 +106,9 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } this.SetFrameMetadata( + metadata, target.Metadata, + x.Index, this.entries[x.Index], x.Compression, bitsPerPixel, @@ -146,7 +148,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals int bpp = 0; for (int i = 0; i < frames.Length; i++) { - BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Bit32; ReadOnlyMemory? colorTable = null; ref IconDirEntry entry = ref this.entries[i]; @@ -198,7 +200,9 @@ internal abstract class IconDecoderCore : IImageDecoderInternals frames[i] = frameMetadata; this.SetFrameMetadata( + metadata, frames[i], + i, this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel, @@ -220,11 +224,13 @@ internal abstract class IconDecoderCore : IImageDecoderInternals metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); } - return new(new(bpp), this.Dimensions, metadata, frames); + return new(this.Dimensions, metadata, frames); } protected abstract void SetFrameMetadata( - ImageFrameMetadata metadata, + ImageMetadata imageMetadata, + ImageFrameMetadata frameMetadata, + int index, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel, diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 243339661..509c9f420 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -167,7 +167,7 @@ internal abstract class IconEncoderCore : IImageEncoderInternals { this.Compression = compression; this.BmpBitsPerPixel = compression == IconFrameCompression.Png - ? BmpBitsPerPixel.Pixel32 + ? BmpBitsPerPixel.Bit32 : bmpBitsPerPixel; this.ColorTable = colorTable; this.iconDirEntry = iconDirEntry; diff --git a/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs index 5d7f84acf..73d114588 100644 --- a/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs @@ -3,7 +3,9 @@ // using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; @@ -121,6 +123,108 @@ public static partial class ImageExtensions encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(BmpFormat.Instance), cancellationToken); + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + public static void SaveAsCur(this Image source, string path) => SaveAsCur(source, path, default); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsCurAsync(this Image source, string path) => SaveAsCurAsync(source, path, default); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsCurAsync(this Image source, string path, CancellationToken cancellationToken) + => SaveAsCurAsync(source, path, default, cancellationToken); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the path is null. + public static void SaveAsCur(this Image source, string path, CurEncoder encoder) => + source.Save( + path, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(CurFormat.Instance)); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsCurAsync(this Image source, string path, CurEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(CurFormat.Instance), + cancellationToken); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsCur(this Image source, Stream stream) + => SaveAsCur(source, stream, default); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsCurAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) + => SaveAsCurAsync(source, stream, default, cancellationToken); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + public static void SaveAsCur(this Image source, Stream stream, CurEncoder encoder) + => source.Save( + stream, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(CurFormat.Instance)); + + /// + /// Saves the image to the given stream with the Cur format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsCurAsync(this Image source, Stream stream, CurEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(CurFormat.Instance), + cancellationToken); + /// /// Saves the image to the given stream with the Gif format. /// @@ -223,6 +327,108 @@ public static partial class ImageExtensions encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(GifFormat.Instance), cancellationToken); + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + public static void SaveAsIco(this Image source, string path) => SaveAsIco(source, path, default); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsIcoAsync(this Image source, string path) => SaveAsIcoAsync(source, path, default); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsIcoAsync(this Image source, string path, CancellationToken cancellationToken) + => SaveAsIcoAsync(source, path, default, cancellationToken); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the path is null. + public static void SaveAsIco(this Image source, string path, IcoEncoder encoder) => + source.Save( + path, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(IcoFormat.Instance)); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsIcoAsync(this Image source, string path, IcoEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(IcoFormat.Instance), + cancellationToken); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsIco(this Image source, Stream stream) + => SaveAsIco(source, stream, default); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsIcoAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) + => SaveAsIcoAsync(source, stream, default, cancellationToken); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + public static void SaveAsIco(this Image source, Stream stream, IcoEncoder encoder) + => source.Save( + stream, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(IcoFormat.Instance)); + + /// + /// Saves the image to the given stream with the Ico format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsIcoAsync(this Image source, Stream stream, IcoEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(IcoFormat.Instance), + cancellationToken); + /// /// Saves the image to the given stream with the Jpeg format. /// diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs index 826f5905b..e35d00ed3 100644 --- a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs +++ b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs @@ -4,7 +4,9 @@ // using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; @@ -40,6 +42,26 @@ public static class ImageMetadataExtensions /// The new public static BmpMetadata CloneBmpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(BmpFormat.Instance); + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static CurMetadata GetCurMetadata(this ImageMetadata source) => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static CurMetadata CloneCurMetadata(this ImageMetadata source) => source.CloneFormatMetadata(CurFormat.Instance); + /// /// Gets the from .
    /// If none is found, an instance is created either by conversion from the decoded image format metadata @@ -60,6 +82,26 @@ public static class ImageMetadataExtensions /// The new public static GifMetadata CloneGifMetadata(this ImageMetadata source) => source.CloneFormatMetadata(GifFormat.Instance); + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image metadata. + /// + /// The + /// + public static IcoMetadata GetIcoMetadata(this ImageMetadata source) => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static IcoMetadata CloneIcoMetadata(this ImageMetadata source) => source.CloneFormatMetadata(IcoFormat.Instance); + /// /// Gets the from .
    /// If none is found, an instance is created either by conversion from the decoded image format metadata @@ -201,6 +243,46 @@ public static class ImageMetadataExtensions public static WebpMetadata CloneWebpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance); + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static CurFrameMetadata CloneCurMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(CurFormat.Instance); + + /// + /// Gets the from .
    + /// If none is found, an instance is created either by conversion from the decoded image format metadata + /// or the requested format default constructor. + /// This instance will be added to the metadata for future requests. + ///
    + /// The image frame metadata. + /// + /// The + /// + public static IcoFrameMetadata GetIcoMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static IcoFrameMetadata CloneIcoMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(IcoFormat.Instance); + /// /// Gets the from .
    /// If none is found, an instance is created either by conversion from the decoded image format metadata diff --git a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude index 24ac66a70..89940d406 100644 --- a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude +++ b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude @@ -5,7 +5,9 @@ <#+ private static readonly string[] formats = new []{ "Bmp", + "Cur", "Gif", + "Ico", "Jpeg", "Pbm", "Png", @@ -16,6 +18,8 @@ }; private static readonly string[] frameFormats = new []{ + "Cur", + "Ico", "Gif", "Png", "Tiff", diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index bcc7484b3..9c0de1edb 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -109,7 +109,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable if (this.DecodedImageFormat is not null && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatFrameMetadata? decodedMetadata)) { - return TFormatFrameMetadata.FromFormatConnectingFrameMetadata(decodedMetadata.ToFormatConnectingFrameMetadata()); + TFormatFrameMetadata derivedMeta = TFormatFrameMetadata.FromFormatConnectingFrameMetadata(decodedMetadata.ToFormatConnectingFrameMetadata()); + this.formatMetadata[key] = derivedMeta; + return derivedMeta; } TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata(); @@ -119,7 +121,7 @@ public sealed class ImageFrameMetadata : IDeepCloneable internal void SetFormatMetadata(IImageFormat key, TFormatFrameMetadata value) where TFormatMetadata : class - where TFormatFrameMetadata : class, IDeepCloneable + where TFormatFrameMetadata : class, IFormatFrameMetadata => this.formatMetadata[key] = value; /// diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 700c7fe83..b5c46d758 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -194,7 +194,9 @@ public sealed class ImageMetadata : IDeepCloneable if (this.DecodedImageFormat is not null && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatMetadata? decodedMetadata)) { - return TFormatMetadata.FromFormatConnectingMetadata(decodedMetadata.ToFormatConnectingMetadata()); + TFormatMetadata derivedMeta = TFormatMetadata.FromFormatConnectingMetadata(decodedMetadata.ToFormatConnectingMetadata()); + this.formatMetadata[key] = derivedMeta; + return derivedMeta; } // Fall back to a default instance. @@ -217,7 +219,7 @@ public sealed class ImageMetadata : IDeepCloneable => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) - where TFormatMetadata : class, IDeepCloneable + where TFormatMetadata : class, IFormatMetadata => this.formatMetadata[key] = value; /// diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index 4efd33648..f7ee7614a 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -23,7 +23,7 @@ public class CurDecoderTests Assert.Equal(image.Width, meta.EncodingWidth); Assert.Equal(image.Height, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } [Theory] @@ -36,6 +36,6 @@ public class CurDecoderTests Assert.Equal(image.Width, meta.EncodingWidth); Assert.Equal(image.Height, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index b9b66296d..59c40c924 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -2,9 +2,11 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Cur; +using static SixLabors.ImageSharp.Tests.TestImages.Ico; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; @@ -17,7 +19,7 @@ public class CurEncoderTests [WithFile(CurReal, PixelTypes.Rgba32)] [WithFile(WindowsMouse, PixelTypes.Rgba32)] public void CanRoundTripEncoder(TestImageProvider provider) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(CurDecoder.Instance); using MemoryStream memStream = new(); @@ -31,4 +33,34 @@ public class CurEncoderTests encoded.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, CurDecoder.Instance); } + + [Theory] + [WithFile(Flutter, PixelTypes.Rgba32)] + public void CanConvertFromIco(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(IcoDecoder.Instance); + using MemoryStream memStream = new(); + + image.Save(memStream, Encoder); + memStream.Seek(0, SeekOrigin.Begin); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider); + + // Despite preservation of the palette. The process can still be lossy + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.TolerantPercentage(.23f), IcoDecoder.Instance); + + for (int i = 0; i < image.Frames.Count; i++) + { + IcoFrameMetadata icoFrame = image.Frames[i].Metadata.GetIcoMetadata(); + CurFrameMetadata curFrame = encoded.Frames[i].Metadata.GetCurMetadata(); + + // Compression may differ as we cannot convert that. + // Color table may differ. + Assert.Equal(icoFrame.BmpBitsPerPixel, curFrame.BmpBitsPerPixel); + Assert.Equal(icoFrame.EncodingWidth, curFrame.EncodingWidth); + Assert.Equal(icoFrame.EncodingHeight, curFrame.EncodingHeight); + } + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index a776a637b..bc46df095 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -56,7 +56,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel1, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit1, meta.BmpBitsPerPixel); } [Theory] @@ -92,7 +92,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel24, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit24, meta.BmpBitsPerPixel); } [Theory] @@ -128,7 +128,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } [Theory] @@ -163,7 +163,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel4, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit4, meta.BmpBitsPerPixel); } [Theory] @@ -199,7 +199,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel8, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit8, meta.BmpBitsPerPixel); } [Theory] @@ -229,7 +229,7 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Png, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } [Theory] @@ -262,7 +262,7 @@ public class IcoDecoderTests { ImageFrame frame = image.Frames[i]; IcoFrameMetadata meta = frame.Metadata.GetIcoMetadata(); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } image.DebugSaveMultiFrame(provider); @@ -327,6 +327,6 @@ public class IcoDecoderTests Assert.Equal(expectedWidth, meta.EncodingWidth); Assert.Equal(expectedHeight, meta.EncodingHeight); Assert.Equal(IconFrameCompression.Bmp, meta.Compression); - Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs index db28f9f70..751db384d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using static SixLabors.ImageSharp.Tests.TestImages.Cur; using static SixLabors.ImageSharp.Tests.TestImages.Ico; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; @@ -26,9 +28,38 @@ public class IcoEncoderTests memStream.Seek(0, SeekOrigin.Begin); using Image encoded = Image.Load(memStream); - encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + encoded.DebugSaveMultiFrame(provider); // Despite preservation of the palette. The process can still be lossy encoded.CompareToOriginalMultiFrame(provider, ImageComparer.TolerantPercentage(.23f), IcoDecoder.Instance); } + + [Theory] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CanConvertFromCur(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(CurDecoder.Instance); + using MemoryStream memStream = new(); + + image.Save(memStream, Encoder); + memStream.Seek(0, SeekOrigin.Begin); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider); + + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, CurDecoder.Instance); + + for (int i = 0; i < image.Frames.Count; i++) + { + CurFrameMetadata curFrame = image.Frames[i].Metadata.GetCurMetadata(); + IcoFrameMetadata icoFrame = encoded.Frames[i].Metadata.GetIcoMetadata(); + + // Compression may differ as we cannot convert that. + Assert.Equal(curFrame.BmpBitsPerPixel, icoFrame.BmpBitsPerPixel); + Assert.Equal(curFrame.EncodingWidth, icoFrame.EncodingWidth); + Assert.Equal(curFrame.EncodingHeight, icoFrame.EncodingHeight); + Assert.Equal(curFrame.ColorTable, icoFrame.ColorTable); + } + } } From 79f38c4badd7e444258fd3906f34eb91c903de61 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Jun 2024 19:50:00 +1000 Subject: [PATCH 203/220] Update BmpEncoder.cs --- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 0be243f9a..e25556804 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Formats.Bmp; From ac5ace789c09f60fd598521bf678f5eb18ac40df Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 3 Jul 2024 21:39:16 +1000 Subject: [PATCH 204/220] Update tests --- .../Processors/Transforms/AffineTransformTests.cs | 9 +++++---- .../Processing/Transforms/ProjectiveTransformTests.cs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index d37d4760f..ab97dfbf6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -252,23 +252,24 @@ public class AffineTransformTests [Fact] public void TransformRotationDoesNotOffset() { - Rgba32 marker = Color.Aqua; + Rgba32 background = Color.DimGray.ToPixel(); + Rgba32 marker = Color.Aqua.ToPixel(); - using Image img = new(100, 100, Color.DimGray); + using Image img = new(100, 100, background); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, Color.DimGray); + using Image img2 = new(100, 100, background); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, Color.DimGray); + using Image img3 = new(100, 100, background); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index 21eda034e..4aec53036 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -188,23 +188,24 @@ public class ProjectiveTransformTests [Fact] public void TransformRotationDoesNotOffset() { - Rgba32 marker = Color.Aqua; + Rgba32 background = Color.DimGray.ToPixel(); + Rgba32 marker = Color.Aqua.ToPixel(); - using Image img = new(100, 100, Color.DimGray); + using Image img = new(100, 100, background); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, Color.DimGray); + using Image img2 = new(100, 100, background); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new ProjectiveTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, Color.DimGray); + using Image img3 = new(100, 100, background); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); From 00b253d1dd4928e6e0b6b9b1f642800471563e16 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 9 Jul 2024 19:45:52 +0200 Subject: [PATCH 205/220] Fix issue in EncodeImage() not using correct histogram image size, fixes #2763 --- src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index f658e40f6..8d682c059 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -698,6 +698,8 @@ internal class Vp8LEncoder : IDisposable } } + histogramImageSize = maxIndex; + this.bitWriter.PutBits((uint)(this.HistoBits - 2), 3); this.EncodeImageNoHuffman( histogramBgra, @@ -713,7 +715,7 @@ internal class Vp8LEncoder : IDisposable // Store Huffman codes. // Find maximum number of symbols for the huffman tree-set. int maxTokens = 0; - for (int i = 0; i < 5 * histogramImage.Count; i++) + for (int i = 0; i < 5 * histogramImageSize; i++) { HuffmanTreeCode codes = huffmanCodes[i]; if (maxTokens < codes.NumSymbols) @@ -728,7 +730,7 @@ internal class Vp8LEncoder : IDisposable tokens[i] = new HuffmanTreeToken(); } - for (int i = 0; i < 5 * histogramImage.Count; i++) + for (int i = 0; i < 5 * histogramImageSize; i++) { HuffmanTreeCode codes = huffmanCodes[i]; this.StoreHuffmanCode(huffTree, tokens, codes); From cc39c502ac931bb117cdac8572f7722516b2ba4b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 9 Jul 2024 19:54:49 +0200 Subject: [PATCH 206/220] Add test case for issue 2763 --- .../Formats/WebP/WebpEncoderTests.cs | 17 +++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Webp/issues/Issue2763.png | 3 +++ 3 files changed, 21 insertions(+) create mode 100644 tests/Images/Input/Webp/issues/Issue2763.png diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index acca49dcf..d1d83ffb9 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -497,6 +497,23 @@ public class WebpEncoderTests image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f)); } + // https://github.com/SixLabors/ImageSharp/issues/2763 + [Theory] + [WithFile(Lossy.Issue2763, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2763(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + WebpEncoder encoder = new() + { + Quality = 84, + FileFormat = WebpFileFormatType.Lossless + }; + + using Image image = provider.GetImage(PngDecoder.Instance); + image.DebugSave(provider); + image.VerifyEncoder(provider, "webp", string.Empty, encoder); + } + public static void RunEncodeLossy_WithPeakImage() { TestImageProvider provider = TestImageProvider.File(TestImageLossyFullPath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8937799e1..2c60105aa 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -821,6 +821,7 @@ public static class TestImages public const string Issue2243 = "Webp/issues/Issue2243.webp"; public const string Issue2257 = "Webp/issues/Issue2257.webp"; public const string Issue2670 = "Webp/issues/Issue2670.webp"; + public const string Issue2763 = "Webp/issues/Issue2763.png"; } } diff --git a/tests/Images/Input/Webp/issues/Issue2763.png b/tests/Images/Input/Webp/issues/Issue2763.png new file mode 100644 index 000000000..6412ed6da --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2763.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb221c5045e9bcbfdb7f4704aa571d910ca0d382fe4748319fe56f4c8c2aab78 +size 429101 From 6c430efd10e745bca8ebdc129090d9f58c2e9112 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 9 Jul 2024 20:02:55 +0200 Subject: [PATCH 207/220] Fix stylecop warning --- src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 8d682c059..3f09f94e3 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -698,7 +698,7 @@ internal class Vp8LEncoder : IDisposable } } - histogramImageSize = maxIndex; + histogramImageSize = maxIndex; this.bitWriter.PutBits((uint)(this.HistoBits - 2), 3); this.EncodeImageNoHuffman( From 2880d721c74e84ef7ad6321678070f28dbb20a3d Mon Sep 17 00:00:00 2001 From: KirillAldashkin Date: Tue, 23 Jul 2024 02:06:24 +0800 Subject: [PATCH 208/220] Fix buffer overrun #2779 --- src/ImageSharp/Image.WrapMemory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index d8cea246f..d3b4e1b66 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -425,7 +425,7 @@ public abstract partial class Image UnmanagedMemoryManager memoryManager = new(pointer, width * height); - Guard.MustBeGreaterThanOrEqualTo(bufferSizeInBytes, memoryManager.Memory.Span.Length, nameof(bufferSizeInBytes)); + Guard.MustBeGreaterThanOrEqualTo(bufferSizeInBytes / sizeof(TPixel), memoryManager.Memory.Span.Length, nameof(bufferSizeInBytes)); MemoryGroup memorySource = MemoryGroup.Wrap(memoryManager.Memory); return new Image(configuration, memorySource, width, height, metadata); From 665ab2fa0602c3535611421b998dac82de2dfe21 Mon Sep 17 00:00:00 2001 From: KirillAldashkin Date: Tue, 23 Jul 2024 02:14:00 +0800 Subject: [PATCH 209/220] Fixed width*height overflows --- src/ImageSharp/Image.WrapMemory.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index d3b4e1b66..03bec8bc6 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -50,7 +50,7 @@ public abstract partial class Image { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); - Guard.IsTrue(pixelMemory.Length >= width * height, nameof(pixelMemory), "The length of the input memory is less than the specified image size"); + Guard.IsTrue(pixelMemory.Length >= (long)width * height, nameof(pixelMemory), "The length of the input memory is less than the specified image size"); MemoryGroup memorySource = MemoryGroup.Wrap(pixelMemory); return new Image(configuration, memorySource, width, height, metadata); @@ -145,7 +145,7 @@ public abstract partial class Image { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); - Guard.IsTrue(pixelMemoryOwner.Memory.Length >= width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size"); + Guard.IsTrue(pixelMemoryOwner.Memory.Length >= (long)width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size"); MemoryGroup memorySource = MemoryGroup.Wrap(pixelMemoryOwner); return new Image(configuration, memorySource, width, height, metadata); @@ -232,7 +232,7 @@ public abstract partial class Image ByteMemoryManager memoryManager = new(byteMemory); - Guard.IsTrue(memoryManager.Memory.Length >= width * height, nameof(byteMemory), "The length of the input memory is less than the specified image size"); + Guard.IsTrue(memoryManager.Memory.Length >= (long)width * height, nameof(byteMemory), "The length of the input memory is less than the specified image size"); MemoryGroup memorySource = MemoryGroup.Wrap(memoryManager.Memory); return new Image(configuration, memorySource, width, height, metadata); @@ -422,6 +422,7 @@ public abstract partial class Image Guard.IsFalse(pointer == null, nameof(pointer), "Pointer must be not null"); Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); + Guard.MustBeLessThanOrEqualTo(height * (long)width, int.MaxValue, "Total amount of pixels exceeds int.MaxValue"); UnmanagedMemoryManager memoryManager = new(pointer, width * height); From 1de220a54381d8d21e6e50429924d8254de32001 Mon Sep 17 00:00:00 2001 From: KirillAldashkin Date: Tue, 23 Jul 2024 03:33:49 +0800 Subject: [PATCH 210/220] Added test cases for width*height overflow when using WrapMemory --- .../Image/ImageTests.WrapMemory.cs | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index f22a55aae..e3c4a7df1 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -294,19 +294,22 @@ public partial class ImageTests } } - [Fact] - public unsafe void WrapMemory_Throws_OnTooLessWrongSize() + [Theory] + [InlineData(20, 5, 5)] + [InlineData(1023, 32, 32)] + [InlineData(65536, 65537, 65536)] + public unsafe void WrapMemory_Throws_OnTooLessWrongSize(int size, int width, int height) { var cfg = Configuration.CreateDefaultInstance(); var metaData = new ImageMetadata(); - var array = new Rgba32[25]; + var array = new Rgba32[size]; Exception thrownException = null; fixed (void* ptr = array) { try { - using var image = Image.WrapMemory(cfg, ptr, 24, 5, 5, metaData); + using var image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData); } catch (Exception e) { @@ -317,24 +320,30 @@ public partial class ImageTests Assert.IsType(thrownException); } - [Fact] - public unsafe void WrapMemory_FromPointer_CreatedImageIsCorrect() + [Theory] + [InlineData(25, 5, 5)] + [InlineData(26, 5, 5)] + [InlineData(2, 1, 1)] + [InlineData(1024, 32, 32)] + [InlineData(2048, 32, 32)] + public unsafe void WrapMemory_FromPointer_CreatedImageIsCorrect(int size, int width, int height) { var cfg = Configuration.CreateDefaultInstance(); var metaData = new ImageMetadata(); - var array = new Rgba32[25]; + var array = new Rgba32[size]; fixed (void* ptr = array) { - using (var image = Image.WrapMemory(cfg, ptr, 25, 5, 5, metaData)) + using (var image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData)) { Assert.True(image.DangerousTryGetSinglePixelMemory(out Memory imageMem)); Span imageSpan = imageMem.Span; + Span sourceSpan = array.AsSpan(0, width * height); ref Rgba32 pixel0 = ref imageSpan[0]; - Assert.True(Unsafe.AreSame(ref array[0], ref pixel0)); + Assert.True(Unsafe.AreSame(ref sourceSpan[0], ref pixel0)); ref Rgba32 pixel_1 = ref imageSpan[imageSpan.Length - 1]; - Assert.True(Unsafe.AreSame(ref array[array.Length - 1], ref pixel_1)); + Assert.True(Unsafe.AreSame(ref sourceSpan[sourceSpan.Length - 1], ref pixel_1)); Assert.Equal(cfg, image.Configuration); Assert.Equal(metaData, image.Metadata); @@ -395,6 +404,7 @@ public partial class ImageTests [InlineData(0, 5, 5)] [InlineData(20, 5, 5)] [InlineData(1023, 32, 32)] + [InlineData(65536, 65537, 65536)] public void WrapMemory_MemoryOfT_InvalidSize(int size, int height, int width) { var array = new Rgba32[size]; @@ -430,6 +440,7 @@ public partial class ImageTests [InlineData(0, 5, 5)] [InlineData(20, 5, 5)] [InlineData(1023, 32, 32)] + [InlineData(65536, 65537, 65536)] public void WrapMemory_IMemoryOwnerOfT_InvalidSize(int size, int height, int width) { var array = new Rgba32[size]; @@ -476,6 +487,7 @@ public partial class ImageTests [InlineData(0, 5, 5)] [InlineData(20, 5, 5)] [InlineData(1023, 32, 32)] + [InlineData(65536, 65537, 65536)] public void WrapMemory_IMemoryOwnerOfByte_InvalidSize(int size, int height, int width) { var array = new byte[size * Unsafe.SizeOf()]; @@ -523,6 +535,7 @@ public partial class ImageTests [InlineData(0, 5, 5)] [InlineData(20, 5, 5)] [InlineData(1023, 32, 32)] + [InlineData(65536, 65537, 65536)] public void WrapMemory_MemoryOfByte_InvalidSize(int size, int height, int width) { var array = new byte[size * Unsafe.SizeOf()]; From 4845281818c23a6cab1e435f004c12ca701acb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20=C5=A0t=C3=A1gl?= Date: Tue, 23 Jul 2024 13:31:31 +0200 Subject: [PATCH 211/220] Fix ImageMetadata typo --- src/ImageSharp/Metadata/ImageMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index e811cc1f7..0c1d05d50 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -165,7 +165,7 @@ public sealed class ImageMetadata : IDeepCloneable public CicpProfile? CicpProfile { get; set; } /// - /// Gets the original format, if any, the image was decode from. + /// Gets the original format, if any, from which the image was decoded. /// public IImageFormat? DecodedImageFormat { get; internal set; } From 9bbc70e5e3c9ff57918141496ff3eddee722e16b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 25 Jul 2024 17:08:53 +1000 Subject: [PATCH 212/220] Cleanup --- src/ImageSharp/Formats/Icon/IconDecoderCore.cs | 10 +++++----- src/ImageSharp/ImageInfo.cs | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 0feed7f69..ce72f1561 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -170,7 +170,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. - ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); + ImageInfo frameInfo = this.GetDecoder(isPng).Identify(stream, cancellationToken); ImageFrameMetadata frameMetadata = new(); @@ -178,14 +178,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals { if (i == 0) { - pngMetadata = temp.Metadata.GetPngMetadata(); + pngMetadata = frameInfo.Metadata.GetPngMetadata(); } - frameMetadata.SetFormatMetadata(PngFormat.Instance, temp.FrameMetadataCollection[0].GetPngMetadata()); + frameMetadata.SetFormatMetadata(PngFormat.Instance, frameInfo.FrameMetadataCollection[0].GetPngMetadata()); } else { - BmpMetadata meta = temp.Metadata.GetBmpMetadata(); + BmpMetadata meta = frameInfo.Metadata.GetBmpMetadata(); bitsPerPixel = meta.BitsPerPixel; colorTable = meta.ColorTable; @@ -210,7 +210,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. - this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); + this.Dimensions = new(Math.Max(this.Dimensions.Width, frameInfo.Size.Width), Math.Max(this.Dimensions.Height, frameInfo.Size.Height)); } // Copy the format specific metadata to the image. diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index 3a2e87017..0bbd73b63 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -62,6 +62,11 @@ public class ImageInfo /// public int Height => this.Size.Height; + /// + /// Gets the number of frames in the image. + /// + public int FrameCount => this.FrameMetadataCollection.Count; + /// /// Gets any metadata associated with the image. /// @@ -75,10 +80,10 @@ public class ImageInfo /// /// Gets the size of the image in px units. /// - public Size Size { get; internal set; } + public Size Size { get; } /// /// Gets the bounds of the image. /// - public Rectangle Bounds => new(0, 0, this.Width, this.Height); + public Rectangle Bounds => new(Point.Empty, this.Size); } From 934a24ad134e5891d1633b92a0f1c2a02c3a4ff7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 31 Jul 2024 22:53:00 +1000 Subject: [PATCH 213/220] Migrate icon codec --- .../Formats/Icon/IconDecoderCore.cs | 24 +++++++++---------- .../Formats/Icon/IconEncoderCore.cs | 4 +++- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index ce72f1561..caed2dd90 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -6,24 +6,21 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconDecoderCore : IImageDecoderInternals +internal abstract class IconDecoderCore : ImageDecoderCore { private IconDir fileHeader; private IconDirEntry[]? entries; protected IconDecoderCore(DecoderOptions options) - => this.Options = options; - - public DecoderOptions Options { get; } - - public Size Dimensions { get; private set; } + : base(options) + { + } - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { // Stream may not at 0. long basePosition = stream.Position; @@ -61,7 +58,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. - Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); + Image temp = this.GetDecoder(isPng).Decode(this.Options.Configuration, stream, cancellationToken); decodedEntries.Add((temp, isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, i)); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data @@ -133,7 +130,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals return result; } - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + /// + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { // Stream may not at 0. long basePosition = stream.Position; @@ -170,7 +168,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. - ImageInfo frameInfo = this.GetDecoder(isPng).Identify(stream, cancellationToken); + ImageInfo frameInfo = this.GetDecoder(isPng).Identify(this.Options.Configuration, stream, cancellationToken); ImageFrameMetadata frameMetadata = new(); @@ -281,7 +279,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals this.Dimensions = new(width, height); } - private IImageDecoderInternals GetDecoder(bool isPng) + private ImageDecoderCore GetDecoder(bool isPng) { if (isPng) { diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 509c9f420..4b973d511 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -11,7 +11,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconEncoderCore : IImageEncoderInternals +internal abstract class IconEncoderCore { private readonly QuantizingImageEncoder encoder; private readonly IconFileType iconFileType; @@ -43,6 +43,8 @@ internal abstract class IconEncoderCore : IImageEncoderInternals for (int i = 0; i < image.Frames.Count; i++) { + cancellationToken.ThrowIfCancellationRequested(); + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. ImageFrame frame = image.Frames[i]; From 1102eb54e97ee137d19e8b0fa796f213b36f0d4c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Aug 2024 11:28:00 +1000 Subject: [PATCH 214/220] Replace PngCrcChunkHandling --- src/ImageSharp/Formats/DecoderOptions.cs | 5 ++++ src/ImageSharp/Formats/Png/PngChunk.cs | 10 +++---- .../Formats/Png/PngCrcChunkHandling.cs | 30 ------------------- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 12 ++++---- .../Formats/Png/PngDecoderOptions.cs | 5 ---- .../Formats/SegmentErrorHandling.cs | 30 +++++++++++++++++++ .../Formats/Png/PngDecoderTests.cs | 18 +++++++++-- 7 files changed, 62 insertions(+), 48 deletions(-) delete mode 100644 src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs create mode 100644 src/ImageSharp/Formats/SegmentErrorHandling.cs diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 6243a071d..be906d7d9 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -55,5 +55,10 @@ public sealed class DecoderOptions ///
    public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } + /// + /// Gets the segment error handling strategy to use during decoding. + /// + public SegmentErrorHandling SegmentErrorHandling { get; init; } = SegmentErrorHandling.IgnoreNonCritical; + internal void SetConfiguration(Configuration configuration) => this.configuration = configuration; } diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 6ec0df9ad..666f51daa 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -41,13 +41,13 @@ internal readonly struct PngChunk /// /// Gets a value indicating whether the given chunk is critical to decoding /// - /// The chunk CRC handling behavior. - public bool IsCritical(PngCrcChunkHandling handling) + /// The segment handling behavior. + public bool IsCritical(SegmentErrorHandling handling) => handling switch { - PngCrcChunkHandling.IgnoreNone => true, - PngCrcChunkHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData, - PngCrcChunkHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette, + SegmentErrorHandling.IgnoreNone => true, + SegmentErrorHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData, + SegmentErrorHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette, _ => false, }; } diff --git a/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs b/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs deleted file mode 100644 index 264d737fd..000000000 --- a/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png; - -/// -/// Specifies how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG. -/// -public enum PngCrcChunkHandling -{ - /// - /// Do not ignore any CRC chunk errors. - /// - IgnoreNone, - - /// - /// Ignore CRC errors in non critical chunks. - /// - IgnoreNonCritical, - - /// - /// Ignore CRC errors in data chunks. - /// - IgnoreData, - - /// - /// Ignore CRC errors in all chunks. - /// - IgnoreAll -} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 01f038141..080c35c0d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -119,7 +119,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// /// How to handle CRC errors. /// - private readonly PngCrcChunkHandling pngCrcChunkHandling; + private readonly SegmentErrorHandling segmentErrorHandling; /// /// A reusable Crc32 hashing instance. @@ -142,7 +142,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.maxFrames = options.GeneralOptions.MaxFrames; this.skipMetadata = options.GeneralOptions.SkipMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; - this.pngCrcChunkHandling = options.PngCrcChunkHandling; + this.segmentErrorHandling = options.GeneralOptions.SegmentErrorHandling; this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } @@ -154,7 +154,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.skipMetadata = true; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; - this.pngCrcChunkHandling = options.PngCrcChunkHandling; + this.segmentErrorHandling = options.GeneralOptions.SegmentErrorHandling; this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } @@ -833,7 +833,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore break; default: - if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll) + if (this.segmentErrorHandling is SegmentErrorHandling.IgnoreData or SegmentErrorHandling.IgnoreAll) { goto EXIT; } @@ -939,7 +939,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore break; default: - if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll) + if (this.segmentErrorHandling is SegmentErrorHandling.IgnoreData or SegmentErrorHandling.IgnoreAll) { goto EXIT; } @@ -1927,7 +1927,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore private void ValidateChunk(in PngChunk chunk, Span buffer) { uint inputCrc = this.ReadChunkCrc(buffer); - if (chunk.IsCritical(this.pngCrcChunkHandling)) + if (chunk.IsCritical(this.segmentErrorHandling)) { Span chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs index abfa4b1da..a73db8777 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs @@ -11,11 +11,6 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions /// public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions(); - /// - /// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG. - /// - public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical; - /// /// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed. /// Defaults to 8MB diff --git a/src/ImageSharp/Formats/SegmentErrorHandling.cs b/src/ImageSharp/Formats/SegmentErrorHandling.cs new file mode 100644 index 000000000..7b28de589 --- /dev/null +++ b/src/ImageSharp/Formats/SegmentErrorHandling.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Specifies how to handle validation of errors in different segments of encoded image files. +/// +public enum SegmentErrorHandling +{ + /// + /// Do not ignore any errors. + /// + IgnoreNone, + + /// + /// Ignore errors in non-critical segments of the encoded image. + /// + IgnoreNonCritical, + + /// + /// Ignore errors in data segments (e.g., image data, metadata). + /// + IgnoreData, + + /// + /// Ignore errors in all segments. + /// + IgnoreAll +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 8492d78f8..c3a6b11f5 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -381,6 +381,20 @@ public partial class PngDecoderTests Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); } + [Theory] + [InlineData(TestImages.Png.Bad.WrongCrcDataChunk, 1)] + [InlineData(TestImages.Png.Bad.Issue2589, 24)] + public void Identify_IgnoreCrcErrors(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(new DecoderOptions() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData }, stream); + + Assert.NotNull(imageInfo); + Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); + } + [Theory] [WithFile(TestImages.Png.Bad.MissingDataChunk, PixelTypes.Rgba32)] public void Decode_MissingDataChunk_ThrowsException(TestImageProvider provider) @@ -479,7 +493,7 @@ public partial class PngDecoderTests public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors(TestImageProvider provider, bool compare) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData }); + using Image image = provider.GetImage(PngDecoder.Instance, new DecoderOptions() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData }); image.DebugSave(provider); if (compare) @@ -660,7 +674,7 @@ public partial class PngDecoderTests public void Binary_PrematureEof() { PngDecoder decoder = PngDecoder.Instance; - PngDecoderOptions options = new() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData }; + PngDecoderOptions options = new() { GeneralOptions = new() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData } }; using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446, decoder, options); // TODO: Try to reduce this to 1. From 4a6b51bc288f13978adcc59f618615216c5db956 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Aug 2024 15:25:41 +1000 Subject: [PATCH 215/220] Rename --- src/ImageSharp/Formats/DecoderOptions.cs | 2 +- src/ImageSharp/Formats/Png/PngChunk.cs | 8 ++++---- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 12 ++++++------ ...tErrorHandling.cs => SegmentIntegrityHandling.cs} | 2 +- .../ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) rename src/ImageSharp/Formats/{SegmentErrorHandling.cs => SegmentIntegrityHandling.cs} (94%) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index be906d7d9..3b16159b7 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -58,7 +58,7 @@ public sealed class DecoderOptions /// /// Gets the segment error handling strategy to use during decoding. /// - public SegmentErrorHandling SegmentErrorHandling { get; init; } = SegmentErrorHandling.IgnoreNonCritical; + public SegmentIntegrityHandling SegmentIntegrityHandling { get; init; } = SegmentIntegrityHandling.IgnoreNonCritical; internal void SetConfiguration(Configuration configuration) => this.configuration = configuration; } diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 666f51daa..3883986d0 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -42,12 +42,12 @@ internal readonly struct PngChunk /// Gets a value indicating whether the given chunk is critical to decoding /// /// The segment handling behavior. - public bool IsCritical(SegmentErrorHandling handling) + public bool IsCritical(SegmentIntegrityHandling handling) => handling switch { - SegmentErrorHandling.IgnoreNone => true, - SegmentErrorHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData, - SegmentErrorHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette, + SegmentIntegrityHandling.IgnoreNone => true, + SegmentIntegrityHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData, + SegmentIntegrityHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette, _ => false, }; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 080c35c0d..484241d52 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -119,7 +119,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// /// How to handle CRC errors. /// - private readonly SegmentErrorHandling segmentErrorHandling; + private readonly SegmentIntegrityHandling segmentIntegrityHandling; /// /// A reusable Crc32 hashing instance. @@ -142,7 +142,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.maxFrames = options.GeneralOptions.MaxFrames; this.skipMetadata = options.GeneralOptions.SkipMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; - this.segmentErrorHandling = options.GeneralOptions.SegmentErrorHandling; + this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling; this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } @@ -154,7 +154,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.skipMetadata = true; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; - this.segmentErrorHandling = options.GeneralOptions.SegmentErrorHandling; + this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling; this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } @@ -833,7 +833,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore break; default: - if (this.segmentErrorHandling is SegmentErrorHandling.IgnoreData or SegmentErrorHandling.IgnoreAll) + if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll) { goto EXIT; } @@ -939,7 +939,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore break; default: - if (this.segmentErrorHandling is SegmentErrorHandling.IgnoreData or SegmentErrorHandling.IgnoreAll) + if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll) { goto EXIT; } @@ -1927,7 +1927,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore private void ValidateChunk(in PngChunk chunk, Span buffer) { uint inputCrc = this.ReadChunkCrc(buffer); - if (chunk.IsCritical(this.segmentErrorHandling)) + if (chunk.IsCritical(this.segmentIntegrityHandling)) { Span chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); diff --git a/src/ImageSharp/Formats/SegmentErrorHandling.cs b/src/ImageSharp/Formats/SegmentIntegrityHandling.cs similarity index 94% rename from src/ImageSharp/Formats/SegmentErrorHandling.cs rename to src/ImageSharp/Formats/SegmentIntegrityHandling.cs index 7b28de589..977aee4ad 100644 --- a/src/ImageSharp/Formats/SegmentErrorHandling.cs +++ b/src/ImageSharp/Formats/SegmentIntegrityHandling.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats; /// /// Specifies how to handle validation of errors in different segments of encoded image files. /// -public enum SegmentErrorHandling +public enum SegmentIntegrityHandling { /// /// Do not ignore any errors. diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index c3a6b11f5..9f3c5f682 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -389,7 +389,7 @@ public partial class PngDecoderTests TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); - ImageInfo imageInfo = Image.Identify(new DecoderOptions() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData }, stream); + ImageInfo imageInfo = Image.Identify(new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData }, stream); Assert.NotNull(imageInfo); Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); @@ -493,7 +493,7 @@ public partial class PngDecoderTests public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors(TestImageProvider provider, bool compare) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder.Instance, new DecoderOptions() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData }); + using Image image = provider.GetImage(PngDecoder.Instance, new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData }); image.DebugSave(provider); if (compare) @@ -674,7 +674,7 @@ public partial class PngDecoderTests public void Binary_PrematureEof() { PngDecoder decoder = PngDecoder.Instance; - PngDecoderOptions options = new() { GeneralOptions = new() { SegmentErrorHandling = SegmentErrorHandling.IgnoreData } }; + PngDecoderOptions options = new() { GeneralOptions = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData } }; using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446, decoder, options); // TODO: Try to reduce this to 1. From 5b5e599fb95bd763846d0df387d5407229062736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20V=C3=A5gs=C3=A6ther=20Karlsen?= Date: Wed, 31 Jul 2024 15:17:24 +0200 Subject: [PATCH 216/220] Add DebuggerDisplayAttribute to ExifValue --- src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs index eacb41cfb..41b947e20 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs @@ -1,10 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; +[DebuggerDisplay("{Tag} = {IsArray?\"[..]\":ToString(),nq} ({GetType().Name,nq})")] internal abstract class ExifValue : IExifValue, IEquatable { protected ExifValue(ExifTag tag) => this.Tag = tag; From 1e4b895e3da611a434fc2ade00b0284f7314d3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20V=C3=A5gs=C3=A6ther=20Karlsen?= Date: Wed, 31 Jul 2024 15:22:30 +0200 Subject: [PATCH 217/220] Add DebuggerDisplayAttribute to IptcValue --- src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs index 1a75ecba2..78f7f6de0 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using System.Text; namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; @@ -8,6 +9,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; /// /// Represents a single value of the IPTC profile. /// +[DebuggerDisplay("{Tag} = {ToString(),nq} ({GetType().Name,nq})")] public sealed class IptcValue : IDeepCloneable { private byte[] data = Array.Empty(); From e8bbdf703ab55acb98f39320b22093f0f20558e1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 4 Aug 2024 15:29:06 +0200 Subject: [PATCH 218/220] Always set YcbcrSubSampling to 1:1 for TiffCompression.Jpeg, fixes issue #2679 --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 7b0f439b1..864452fd2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -620,7 +620,12 @@ internal static class TiffDecoderOptionsParser } options.CompressionType = TiffDecoderCompressionType.OldJpeg; - AdjustOptionsYCbCrJpegCompression(options); + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + options.ColorType = TiffColorType.Rgb; + } break; } @@ -628,7 +633,20 @@ internal static class TiffDecoderOptionsParser case TiffCompression.Jpeg: { options.CompressionType = TiffDecoderCompressionType.Jpeg; - AdjustOptionsYCbCrJpegCompression(options); + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) + { + // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + options.ColorType = TiffColorType.Rgb; + } + + // Some tiff encoder set this to values different from [1, 1]. The jpeg decoder already handles this, + // so we set this always to [1, 1], see: https://github.com/SixLabors/ImageSharp/issues/2679 + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + { + options.YcbcrSubSampling[0] = 1; + options.YcbcrSubSampling[1] = 1; + } break; } @@ -647,24 +665,6 @@ internal static class TiffDecoderOptionsParser } } - private static void AdjustOptionsYCbCrJpegCompression(TiffDecoderCore options) - { - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) - { - // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. - options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - options.ColorType = TiffColorType.Rgb; - } - - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) - { - // Some tiff encoder set this to values different from [1, 1]. The jpeg decoder already handles this, - // so we set this always to [1, 1], see: https://github.com/SixLabors/ImageSharp/issues/2679 - options.YcbcrSubSampling[0] = 1; - options.YcbcrSubSampling[1] = 1; - } - } - private static bool IsBiColorCompression(TiffCompression? compression) { if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or From 978aff8208bd904b1bbd497f664e1389a7184d3b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 4 Aug 2024 17:22:57 +0200 Subject: [PATCH 219/220] Add test case for issue 2679 --- .../Formats/Tiff/TiffDecoderTests.cs | 17 +++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + ...de_JpegCompressedWithIssue2679_Issue2679.png | 3 +++ tests/Images/Input/Tiff/Issues/Issue2679.tiff | 3 +++ 4 files changed, 24 insertions(+) create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png create mode 100644 tests/Images/Input/Tiff/Issues/Issue2679.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ab49805a3..97f02f368 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -671,6 +671,23 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_BiColorWithMissingBitsPerSample(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + // https://github.com/SixLabors/ImageSharp/issues/2679 + [Theory] + [WithFile(Issues2679, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_JpegCompressedWithIssue2679(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + + // The image is handcrafted to simulate issue 2679. ImageMagick will throw an expection here and wont decode, + // so we compare to rererence output instead. + image.DebugSave(provider); + image.CompareToReferenceOutput( + ImageComparer.Exact, + provider, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)] public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ac7079268..772437c4b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1032,6 +1032,7 @@ public static class TestImages public const string Issues2255 = "Tiff/Issues/Issue2255.png"; public const string Issues2435 = "Tiff/Issues/Issue2435.tiff"; public const string Issues2587 = "Tiff/Issues/Issue2587.tiff"; + public const string Issues2679 = "Tiff/Issues/Issue2679.tiff"; public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff"; public const string Tiled0000023664 = "Tiff/Issues/tiled-0000023664.tiff"; diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png new file mode 100644 index 000000000..6150aacb3 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cd36c7e07a08e22cceecd28a056c80e5553a8c092bfc091e902d13bd5c46f4d +size 120054 diff --git a/tests/Images/Input/Tiff/Issues/Issue2679.tiff b/tests/Images/Input/Tiff/Issues/Issue2679.tiff new file mode 100644 index 000000000..1bc49f079 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2679.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:feb938396b9d5b4c258244197ba382937a52c93f72cc91081c7e6810e4a3b94c +size 6136 From 5c60126377aefbf089ccf365df0880d3006b3c63 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 4 Aug 2024 17:35:33 +0200 Subject: [PATCH 220/220] Add check, if YcbcrSubSampling has a value --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 864452fd2..c3ff758ac 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -633,21 +633,22 @@ internal static class TiffDecoderOptionsParser case TiffCompression.Jpeg: { options.CompressionType = TiffDecoderCompressionType.Jpeg; - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) - { - // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. - options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - options.ColorType = TiffColorType.Rgb; - } // Some tiff encoder set this to values different from [1, 1]. The jpeg decoder already handles this, // so we set this always to [1, 1], see: https://github.com/SixLabors/ImageSharp/issues/2679 - if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr) + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.YcbcrSubSampling != null) { options.YcbcrSubSampling[0] = 1; options.YcbcrSubSampling[1] = 1; } + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) + { + // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + options.ColorType = TiffColorType.Rgb; + } + break; }